UNPKG

@shopify/hydrogen-react

Version:

React components, hooks, and utilities for creating custom Shopify storefronts

384 lines (383 loc) • 11.5 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const jsxRuntime = require("react/jsx-runtime"); const React = require("react"); function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (e) { for (const k in e) { if (k !== "default") { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React); const IMAGE_FRAGMENT = `#graphql fragment Image on Image { altText url width height } `; const Image = React__namespace.forwardRef( ({ alt, aspectRatio, crop = "center", data, decoding = "async", height = "auto", loader = shopifyLoader, loading = "lazy", sizes, src, srcSetOptions = { intervals: 15, startingWidth: 200, incrementSize: 200, placeholderWidth: 100 }, width = "100%", ...passthroughProps }, ref) => { const normalizedData = React__namespace.useMemo(() => { const dataWidth = (data == null ? void 0 : data.width) && (data == null ? void 0 : data.height) ? data == null ? void 0 : data.width : void 0; const dataHeight = (data == null ? void 0 : data.width) && (data == null ? void 0 : data.height) ? data == null ? void 0 : data.height : void 0; return { width: dataWidth, height: dataHeight, unitsMatch: Boolean(unitsMatch(dataWidth, dataHeight)) }; }, [data]); const normalizedProps = React__namespace.useMemo(() => { const nWidthProp = width || "100%"; const widthParts = getUnitValueParts(nWidthProp.toString()); const nWidth = `${widthParts.number}${widthParts.unit}`; const autoHeight = height === void 0 || height === null; const heightParts = autoHeight ? null : getUnitValueParts(height.toString()); const fixedHeight = heightParts ? `${heightParts.number}${heightParts.unit}` : ""; const nHeight = autoHeight ? "auto" : fixedHeight; const nSrc = src || (data == null ? void 0 : data.url); if (!nSrc) { console.warn( `No src or data.url provided to Image component.`, (passthroughProps == null ? void 0 : passthroughProps.key) || "" ); } const nAlt = (data == null ? void 0 : data.altText) && !alt ? data == null ? void 0 : data.altText : alt || ""; const nAspectRatio = aspectRatio ? aspectRatio : normalizedData.unitsMatch ? [ getNormalizedFixedUnit(normalizedData.width), getNormalizedFixedUnit(normalizedData.height) ].join("/") : void 0; return { width: nWidth, height: nHeight, src: nSrc, alt: nAlt, aspectRatio: nAspectRatio }; }, [ width, height, src, data, alt, aspectRatio, normalizedData, passthroughProps == null ? void 0 : passthroughProps.key ]); const { intervals, startingWidth, incrementSize, placeholderWidth } = srcSetOptions; const imageWidths = React__namespace.useMemo(() => { return generateImageWidths( width, intervals, startingWidth, incrementSize ); }, [width, intervals, startingWidth, incrementSize]); const fixedWidth = isFixedWidth(normalizedProps.width); if (!sizes && !fixedWidth) { console.warn( [ "No sizes prop provided to Image component,", "you may be loading unnecessarily large images.", `Image used is ${src || (data == null ? void 0 : data.url) || (passthroughProps == null ? void 0 : passthroughProps.key) || "unknown"}` ].join(" ") ); } if (fixedWidth) { return /* @__PURE__ */ jsxRuntime.jsx( FixedWidthImage, { aspectRatio, crop, decoding, height, imageWidths, loader, loading, normalizedProps, passthroughProps, ref, width, data } ); } else { return /* @__PURE__ */ jsxRuntime.jsx( FluidImage, { aspectRatio, crop, decoding, imageWidths, loader, loading, normalizedProps, passthroughProps, placeholderWidth, ref, sizes, data } ); } } ); const FixedWidthImage = React__namespace.forwardRef( ({ aspectRatio, crop, decoding, height, imageWidths, loader = shopifyLoader, loading, normalizedProps, passthroughProps, width, data }, ref) => { const fixed = React__namespace.useMemo(() => { const intWidth = getNormalizedFixedUnit(width); const intHeight = getNormalizedFixedUnit(height); const fixedAspectRatio = aspectRatio ? aspectRatio : unitsMatch(normalizedProps.width, normalizedProps.height) ? [intWidth, intHeight].join("/") : normalizedProps.aspectRatio ? normalizedProps.aspectRatio : void 0; const sizesArray = imageWidths === void 0 ? void 0 : generateSizes(imageWidths, fixedAspectRatio, crop, { width: (data == null ? void 0 : data.width) ?? void 0, height: (data == null ? void 0 : data.height) ?? void 0 }); const fixedHeight = intHeight ? intHeight : fixedAspectRatio && intWidth ? intWidth * (parseAspectRatio(fixedAspectRatio) ?? 1) : void 0; const srcSet = generateSrcSet(normalizedProps.src, sizesArray, loader); const src = loader({ src: normalizedProps.src, width: intWidth, height: fixedHeight, crop: normalizedProps.height === "auto" ? void 0 : crop }); return { width: intWidth, aspectRatio: fixedAspectRatio, height: fixedHeight, srcSet, src }; }, [ aspectRatio, crop, data, height, imageWidths, loader, normalizedProps, width ]); return /* @__PURE__ */ jsxRuntime.jsx( "img", { ref, alt: normalizedProps.alt, decoding, height: fixed.height, loading, src: fixed.src, srcSet: fixed.srcSet, width: fixed.width, style: { aspectRatio: fixed.aspectRatio, ...passthroughProps.style }, ...passthroughProps } ); } ); const FluidImage = React__namespace.forwardRef( ({ crop, decoding, imageWidths, loader = shopifyLoader, loading, normalizedProps, passthroughProps, placeholderWidth, sizes, data }, ref) => { const fluid = React__namespace.useMemo(() => { const sizesArray = imageWidths === void 0 ? void 0 : generateSizes(imageWidths, normalizedProps.aspectRatio, crop, { width: (data == null ? void 0 : data.width) ?? void 0, height: (data == null ? void 0 : data.height) ?? void 0 }); const placeholderHeight = normalizedProps.aspectRatio && placeholderWidth ? placeholderWidth * (parseAspectRatio(normalizedProps.aspectRatio) ?? 1) : void 0; const srcSet = generateSrcSet(normalizedProps.src, sizesArray, loader); const src = loader({ src: normalizedProps.src, width: placeholderWidth, height: placeholderHeight, crop }); return { placeholderHeight, srcSet, src }; }, [crop, data, imageWidths, loader, normalizedProps, placeholderWidth]); return /* @__PURE__ */ jsxRuntime.jsx( "img", { ref, alt: normalizedProps.alt, decoding, height: fluid.placeholderHeight, loading, sizes, src: fluid.src, srcSet: fluid.srcSet, width: placeholderWidth, ...passthroughProps, style: { width: normalizedProps.width, aspectRatio: normalizedProps.aspectRatio, ...passthroughProps.style } } ); } ); const PLACEHOLDER_DOMAIN = "https://placeholder.shopify.com"; function shopifyLoader({ src, width, height, crop }) { if (!src) { return ""; } const url = new URL(src, PLACEHOLDER_DOMAIN); if (width) { url.searchParams.append("width", Math.round(width).toString()); } if (height) { url.searchParams.append("height", Math.round(height).toString()); } if (crop) { url.searchParams.append("crop", crop); } return url.href.replace(PLACEHOLDER_DOMAIN, ""); } function unitsMatch(width = "100%", height = "auto") { return getUnitValueParts(width.toString()).unit === getUnitValueParts(height.toString()).unit; } function getUnitValueParts(value) { const unit = value.replace(/[0-9.]/g, ""); const number = parseFloat(value.replace(unit, "")); return { unit: unit === "" ? number === void 0 ? "auto" : "px" : unit, number }; } function getNormalizedFixedUnit(value) { if (value === void 0) { return; } const { unit, number } = getUnitValueParts(value.toString()); switch (unit) { case "em": return number * 16; case "rem": return number * 16; case "px": return number; case "": return number; default: return; } } function isFixedWidth(width) { const fixedEndings = /\d(px|em|rem)$/; return typeof width === "number" || fixedEndings.test(width); } function generateSrcSet(src, sizesArray, loader = shopifyLoader) { if (!src) { return ""; } if ((sizesArray == null ? void 0 : sizesArray.length) === 0 || !sizesArray) { return src; } return sizesArray.map( (size, i) => `${loader({ src, width: size.width, height: size.height, crop: size.crop })} ${sizesArray.length === 3 ? `${i + 1}x` : `${size.width ?? 0}w`}` ).join(`, `); } function generateImageWidths(width = "100%", intervals, startingWidth, incrementSize) { const responsive = Array.from( { length: intervals }, (_, i) => i * incrementSize + startingWidth ); const fixed = Array.from( { length: 3 }, (_, i) => (i + 1) * (getNormalizedFixedUnit(width) ?? 0) ); return isFixedWidth(width) ? fixed : responsive; } function parseAspectRatio(aspectRatio) { if (!aspectRatio) return; const [width, height] = aspectRatio.split("/"); return 1 / (Number(width) / Number(height)); } function generateSizes(imageWidths, aspectRatio, crop = "center", sourceDimensions) { if (!imageWidths) return; return imageWidths.map((width) => { return { width, height: aspectRatio ? width * (parseAspectRatio(aspectRatio) ?? 1) : void 0, crop }; }).filter(({ width, height }) => { if ((sourceDimensions == null ? void 0 : sourceDimensions.width) && width > sourceDimensions.width) { return false; } if ((sourceDimensions == null ? void 0 : sourceDimensions.height) && height && height > sourceDimensions.height) { return false; } return true; }); } exports.IMAGE_FRAGMENT = IMAGE_FRAGMENT; exports.Image = Image; exports.generateImageWidths = generateImageWidths; exports.generateSizes = generateSizes; exports.generateSrcSet = generateSrcSet; exports.parseAspectRatio = parseAspectRatio; exports.shopifyLoader = shopifyLoader; //# sourceMappingURL=Image.js.map