UNPKG

react-ronin

Version:

Access the RONIN data platform via React.

74 lines (73 loc) 3.54 kB
"use client"; // We are purposefully importing `React` here, as the build output contains // references to it, and those would fail if we don't import it explicitly. import React, { useCallback, useRef, forwardRef } from "react"; const supportedFitValues = ["fill", "contain", "cover"]; const Image = forwardRef(({ src: input, alt, size: defaultSize, width: defaultWidth, height: defaultHeight, fit = "cover", format = "webp", quality = 80, aspect, loading, style, className, }, ref) => { const imageElement = useRef(null); const renderTime = useRef(Date.now()); const isMediaObject = typeof input === "object" && input !== null; const width = defaultSize || defaultWidth; const height = defaultSize || defaultHeight; const onLoad = useCallback(() => { const duration = Date.now() - renderTime.current; const threshold = 150; // Fade in and gradually reduce blur of the real image if loading takes // longer than the specified threshold. if (duration > threshold) { imageElement.current?.animate([ { filter: "blur(4px)", opacity: 0 }, { filter: "blur(0px)", opacity: 1 }, ], { duration: 200, }); } }, []); if (!height && !width) throw new Error("Either `width`, `height`, or `size` must be defined for `Image`."); // Validate given `quality` property. if (quality && (quality < 0 || quality > 100)) throw new Error("The given `quality` was not in the range between 0 and 100."); const optimizationParams = new URLSearchParams({ ...(width ? { w: width.toString() } : {}), ...(height ? { h: height.toString() } : {}), ...(format !== "original" ? { fm: format } : {}), fit: supportedFitValues.includes(fit) ? fit : "cover", q: quality.toString(), }); const responsiveOptimizationParams = new URLSearchParams({ ...(width ? { h: (width * 2).toString() } : {}), ...(height ? { h: (height * 2).toString() } : {}), ...(format !== "original" ? { fm: format } : {}), fit: supportedFitValues.includes(fit) ? fit : "cover", q: quality.toString(), }); const source = isMediaObject ? `${input.src}?${optimizationParams}` : input; const responsiveSource = isMediaObject ? `${input.src}?${optimizationParams} 1x, ` + `${input.src}?${responsiveOptimizationParams} 2x` : input; const placeholder = input && typeof input !== "string" ? input.placeholder?.base64 : null; return (React.createElement("div", { ref: ref, className: className, style: { position: "relative", overflow: "hidden", flexShrink: 0, width: width || "100%", height: height || "100%", aspectRatio: aspect === "video" ? "16/9" : aspect === "square" ? "1/1" : "auto", ...style, } }, placeholder && (React.createElement("img", { style: { position: "absolute", width: "100%", height: "100%", objectFit: fit, }, src: placeholder, alt: alt })), React.createElement("img", { alt: alt, style: { position: "absolute", width: "100%", height: "100%", objectFit: fit, }, decoding: "async", onLoad: onLoad, loading: loading, ref: imageElement, src: source, srcSet: responsiveSource }))); }); export default Image;