UNPKG

react-img-toolkit

Version:

A lightweight React library for optimizing image loading through preloading, lazy loading, and caching capabilities

401 lines (383 loc) 16.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var defaultExtractUrls = function (data) { if (!data) return []; // Handle string input if (typeof data === 'string') return [data]; // Handle array of strings or objects with url property if (Array.isArray(data)) { return data.map(function (item) { return typeof item === 'string' ? item : (item === null || item === void 0 ? void 0 : item.url) || ''; }).filter(Boolean); } // Handle object with url property return data.url ? [data.url] : []; }; var preloadMedia = function (url, mediaType) { return new Promise(function (resolve, reject) { if (mediaType === 'video') { var video = document.createElement('video'); video.preload = 'auto'; video.onloadeddata = function () { return resolve(url); }; video.onerror = function () { return reject(new Error("Failed to load video: ".concat(url))); }; video.src = url; } else { var img = new Image(); img.onload = function () { return resolve(url); }; img.onerror = function () { return reject(new Error("Failed to load image: ".concat(url))); }; img.src = url; } }); }; var ImagePreloader = function (_a) { var data = _a.data, onSuccess = _a.onSuccess, onError = _a.onError, children = _a.children, _b = _a.mediaType, mediaType = _b === void 0 ? 'image' : _b, _c = _a.extractUrls, extractUrls = _c === void 0 ? defaultExtractUrls : _c; var _d = React.useState(true), loading = _d[0], setLoading = _d[1]; var _e = React.useState(null), error = _e[0], setError = _e[1]; var _f = React.useState(0), count = _f[0], setCount = _f[1]; React.useEffect(function () { var urls = extractUrls(data); if (!urls.length) { setLoading(false); onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(); return; } setLoading(true); setError(null); setCount(0); var getMediaType = function (url) { if (typeof mediaType === 'function') { return mediaType(url); } return mediaType; }; var preloadAll = function () { return __awaiter(void 0, void 0, void 0, function () { var err_1, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, 3, 4]); return [4 /*yield*/, Promise.all(urls.map(function (url) { return preloadMedia(url, getMediaType(url)) .then(function () { return setCount(function (prev) { return prev + 1; }); }); }))]; case 1: _a.sent(); onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(); return [3 /*break*/, 4]; case 2: err_1 = _a.sent(); error_1 = err_1 instanceof Error ? err_1 : new Error('Failed to preload media'); setError(error_1); onError === null || onError === void 0 ? void 0 : onError(error_1); return [3 /*break*/, 4]; case 3: setLoading(false); return [7 /*endfinally*/]; case 4: return [2 /*return*/]; } }); }); }; preloadAll(); return function () { // Cleanup if needed }; }, [data, mediaType, onSuccess, onError, extractUrls]); // If children is a function, pass the loading state and error if (typeof children === 'function') { return React__default["default"].createElement(React__default["default"].Fragment, null, children({ loading: loading, error: error, count: count })); } // Otherwise, just render children when not loading and no error if (loading || error) return null; return React__default["default"].createElement(React__default["default"].Fragment, null, children); }; var useImagePreloader = function (urls, options) { if (options === void 0) { options = {}; } var _a = React.useState([]), urlsState = _a[0], setUrlsState = _a[1]; var _b = React.useState(0), count = _b[0], setCount = _b[1]; var _c = React.useState(false), loaded = _c[0], setLoaded = _c[1]; var _d = React.useState(null), error = _d[0], setError = _d[1]; var getMediaType = function (url) { if (typeof options.mediaType === 'function') { return options.mediaType(url); } return options.mediaType || 'image'; }; var preloadMedia = function (url) { var mediaType = getMediaType(url); if (mediaType === 'video') { return new Promise(function (resolve, reject) { var video = document.createElement('video'); video.preload = 'auto'; video.onloadeddata = function () { return resolve(url); }; video.onerror = function () { return reject(new Error("Failed to load video: ".concat(url))); }; video.src = url; }); } // Default to image preloading return new Promise(function (resolve, reject) { var img = new Image(); img.onload = function () { return resolve(url); }; img.onerror = function () { return reject(new Error("Failed to load image: ".concat(url))); }; img.src = url; }); }; React.useEffect(function () { if (!urls.length) return; setLoaded(false); setError(null); var mediaPromises = urls.map(function (url) { return preloadMedia(url); }); Promise.all(mediaPromises) .then(function (loadedUrls) { var _a; setUrlsState(loadedUrls); setCount(loadedUrls.length); setLoaded(true); (_a = options.onSuccess) === null || _a === void 0 ? void 0 : _a.call(options); }) .catch(function (err) { var _a; setError(err); (_a = options.onError) === null || _a === void 0 ? void 0 : _a.call(options, err); }); return function () { setUrlsState([]); setCount(0); setLoaded(false); setError(null); }; }, [urls, options]); return { urls: urlsState, count: count, loaded: loaded, error: error }; }; var useImageStatus = function (src) { var _a = React.useState('idle'), status = _a[0], setStatus = _a[1]; React.useEffect(function () { if (!src) { setStatus('idle'); return; } setStatus('loading'); var img = new Image(); img.onload = function () { setStatus('loaded'); }; img.onerror = function () { setStatus('error'); }; img.src = src; return function () { img.onload = null; img.onerror = null; }; }, [src]); return status; }; var useLazyImage = function (src, options) { if (options === void 0) { options = {}; } var _a = React.useState(false), isIntersecting = _a[0], setIsIntersecting = _a[1]; var _b = React.useState(false), isLoaded = _b[0], setIsLoaded = _b[1]; var ref = React.useRef(null); React.useEffect(function () { var element = ref.current; if (!element) return; var observer = new IntersectionObserver(function (_a) { var entry = _a[0]; setIsIntersecting(entry.isIntersecting); }, { threshold: options.threshold || 0, rootMargin: options.rootMargin || '0px' }); observer.observe(element); return function () { observer.unobserve(element); observer.disconnect(); }; }, [options.threshold, options.rootMargin]); React.useEffect(function () { if (!isIntersecting || isLoaded) return; var img = new Image(); img.src = src; img.onload = function () { setIsLoaded(true); }; return function () { img.onload = null; }; }, [isIntersecting, isLoaded, src]); return { isIntersecting: isIntersecting, isLoaded: isLoaded, ref: ref }; }; // Global cache to store loaded image URLs var imageCache = new Map(); var useImageCache = function (src) { var _a = React.useState(!imageCache.has(src)), loading = _a[0], setLoading = _a[1]; var _b = React.useState(imageCache.has(src)), isCached = _b[0], setIsCached = _b[1]; React.useEffect(function () { if (imageCache.has(src)) { setLoading(false); setIsCached(true); return; } var img = new Image(); img.onload = function () { imageCache.set(src, true); setLoading(false); setIsCached(true); }; img.onerror = function () { setLoading(false); }; img.src = src; return function () { img.onload = null; img.onerror = null; }; }, [src]); return { cachedSrc: src, loading: loading, isCached: isCached }; }; var isHtmlString = function (str) { return /<\/?[a-z][\s\S]*>/i.test(str); }; var extractImageUrlsFromHtml = function (html) { var imgUrls = []; var imgRegex = /<img[^>]+src="([^">]+)"/g; var match; while ((match = imgRegex.exec(html)) !== null) { imgUrls.push(match[1]); } return imgUrls; }; var extractImageUrlsFromData = function (data) { var urls = []; if (Array.isArray(data)) { data.forEach(function (item) { urls = urls.concat(extractImageUrlsFromData(item)); }); } else if (typeof data === "object" && data !== null) { for (var key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { urls = urls.concat(extractImageUrlsFromData(data[key])); } } } else if (typeof data === "string") { if (isHtmlString(data)) { urls = urls.concat(extractImageUrlsFromHtml(data)); } else if (data.startsWith("http")) { urls.push(data); } } return urls; }; var preloadImages = function (imageUrls) { return __awaiter(void 0, void 0, void 0, function () { var promises; return __generator(this, function (_a) { switch (_a.label) { case 0: promises = imageUrls.map(function (url) { return new Promise(function (resolve, reject) { var img = new Image(); img.src = url; img.onload = resolve; img.onerror = function (error) { console.error("Failed to load image at ".concat(url), error); reject(error); }; }); }); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, Promise.all(promises)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: _a.sent(); console.error("Some images failed to load."); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; exports.ImagePreloader = ImagePreloader; exports.extractImageUrlsFromData = extractImageUrlsFromData; exports.extractImageUrlsFromHtml = extractImageUrlsFromHtml; exports.isHtmlString = isHtmlString; exports.preloadImages = preloadImages; exports.useImageCache = useImageCache; exports.useImagePreloader = useImagePreloader; exports.useImageStatus = useImageStatus; exports.useLazyImage = useLazyImage; //# sourceMappingURL=index.js.map