{"version":3,"file":"Image.mjs","sources":["../../src/Image.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n  getShopifyImageDimensions,\n  shopifyImageLoader,\n  addImageSizeParametersToUrl,\n  IMG_SRC_SET_SIZES,\n} from './image-size.js';\nimport type {Image as ImageType} from './storefront-api-types.js';\nimport type {PartialDeep, Simplify} from 'type-fest';\n\ntype HtmlImageProps = React.ImgHTMLAttributes<HTMLImageElement>;\n\nexport type ShopifyLoaderOptions = {\n  crop?: 'top' | 'bottom' | 'left' | 'right' | 'center';\n  scale?: 2 | 3;\n  width?: HtmlImageProps['width'] | ImageType['width'];\n  height?: HtmlImageProps['height'] | ImageType['height'];\n};\nexport type ShopifyLoaderParams = Simplify<\n  ShopifyLoaderOptions & {\n    src: ImageType['url'];\n  }\n>;\nexport type ShopifyImageProps = Omit<HtmlImageProps, 'src'> & {\n  /** An object with fields that correspond to the Storefront API's\n   * [Image object](https://shopify.dev/api/storefront/reference/common-objects/image).\n   * The `data` prop is required.\n   */\n  data: PartialDeep<ImageType, {recurseIntoArrays: true}>;\n  /** A custom function that generates the image URL. Parameters passed in\n   * are `ShopifyLoaderParams`\n   */\n  loader?: (params: ShopifyLoaderParams) => string;\n  /** An object of `loader` function options. For example, if the `loader` function\n   * requires a `scale` option, then the value can be a property of the\n   * `loaderOptions` object (for example, `{scale: 2}`). The object shape is `ShopifyLoaderOptions`.\n   */\n  loaderOptions?: ShopifyLoaderOptions;\n  /**\n   * `src` isn't used, and should instead be passed as part of the `data` object\n   */\n  src?: never;\n  /**\n   * An array of pixel widths to overwrite the default generated srcset. For example, `[300, 600, 800]`.\n   */\n  widths?: (HtmlImageProps['width'] | ImageType['width'])[];\n};\n\n/**\n * The `Image` component renders an image for the Storefront API's\n * [Image object](https://shopify.dev/api/storefront/reference/common-objects/image) by using the `data` prop. You can [customize this component](https://shopify.dev/api/hydrogen/components#customizing-hydrogen-components) using passthrough props.\n *\n * An image's width and height are determined using the following priority list:\n * 1. The width and height values for the `loaderOptions` prop\n * 2. The width and height values for bare props\n * 3. The width and height values for the `data` prop\n *\n * If only one of `width` or `height` are defined, then the other will attempt to be calculated based on the image's aspect ratio,\n * provided that both `data.width` and `data.height` are available. If `data.width` and `data.height` aren't available, then the aspect ratio cannot be determined and the missing\n * value will remain as `null`\n */\nexport function Image({\n  data,\n  width,\n  height,\n  loading,\n  loader = shopifyImageLoader,\n  loaderOptions,\n  widths,\n  decoding = 'async',\n  ...rest\n}: ShopifyImageProps) {\n  if (!data.url) {\n    const missingUrlError = `<Image/>: the 'data' prop requires the 'url' property. Image: ${\n      data.id ?? 'no ID provided'\n    }`;\n\n    if (__HYDROGEN_DEV__) {\n      throw new Error(missingUrlError);\n    } else {\n      console.error(missingUrlError);\n    }\n\n    return null;\n  }\n\n  if (__HYDROGEN_DEV__ && !data.altText && !rest.alt) {\n    console.warn(\n      `<Image/>: the 'data' prop should have the 'altText' property, or the 'alt' prop, and one of them should not be empty. Image: ${\n        data.id ?? data.url\n      }`\n    );\n  }\n\n  const {width: imgElementWidth, height: imgElementHeight} =\n    getShopifyImageDimensions({\n      data,\n      loaderOptions,\n      elementProps: {\n        width,\n        height,\n      },\n    });\n\n  if (__HYDROGEN_DEV__ && (!imgElementWidth || !imgElementHeight)) {\n    console.warn(\n      `<Image/>: the 'data' prop requires either 'width' or 'data.width', and 'height' or 'data.height' properties. Image: ${\n        data.id ?? data.url\n      }`\n    );\n  }\n\n  let finalSrc = data.url;\n\n  if (loader) {\n    finalSrc = loader({\n      ...loaderOptions,\n      src: data.url,\n      width: imgElementWidth,\n      height: imgElementHeight,\n    });\n    if (typeof finalSrc !== 'string' || !finalSrc) {\n      throw new Error(\n        `<Image/>: 'loader' did not return a valid string. Image: ${\n          data.id ?? data.url\n        }`\n      );\n    }\n  }\n\n  // determining what the intended width of the image is. For example, if the width is specified and lower than the image width, then that is the maximum image width\n  // to prevent generating a srcset with widths bigger than needed or to generate images that would distort because of being larger than original\n  const maxWidth =\n    width && imgElementWidth && width < imgElementWidth\n      ? width\n      : imgElementWidth;\n  const finalSrcset =\n    rest.srcSet ??\n    internalImageSrcSet({\n      ...loaderOptions,\n      widths,\n      src: data.url,\n      width: maxWidth,\n      height: imgElementHeight,\n      loader,\n    });\n\n  /* eslint-disable hydrogen/prefer-image-component */\n  return (\n    <img\n      id={data.id ?? ''}\n      alt={data.altText ?? rest.alt ?? ''}\n      loading={loading ?? 'lazy'}\n      {...rest}\n      src={finalSrc}\n      width={imgElementWidth ?? undefined}\n      height={imgElementHeight ?? undefined}\n      srcSet={finalSrcset}\n      decoding={decoding}\n    />\n  );\n  /* eslint-enable hydrogen/prefer-image-component */\n}\n\ntype InternalShopifySrcSetGeneratorsParams = Simplify<\n  ShopifyLoaderOptions & {\n    src: ImageType['url'];\n    widths?: (HtmlImageProps['width'] | ImageType['width'])[];\n    loader?: (params: ShopifyLoaderParams) => string;\n  }\n>;\nfunction internalImageSrcSet({\n  src,\n  width,\n  crop,\n  scale,\n  widths,\n  loader,\n  height,\n}: InternalShopifySrcSetGeneratorsParams) {\n  const hasCustomWidths = widths && Array.isArray(widths);\n  if (hasCustomWidths && widths.some((size) => isNaN(size as number))) {\n    throw new Error(\n      `<Image/>: the 'widths' must be an array of numbers. Image: ${src}`\n    );\n  }\n\n  let aspectRatio = 1;\n  if (width && height) {\n    aspectRatio = Number(height) / Number(width);\n  }\n\n  let setSizes = hasCustomWidths ? widths : IMG_SRC_SET_SIZES;\n  if (\n    !hasCustomWidths &&\n    width &&\n    width < IMG_SRC_SET_SIZES[IMG_SRC_SET_SIZES.length - 1]\n  ) {\n    setSizes = IMG_SRC_SET_SIZES.filter((size) => size <= width);\n  }\n  const srcGenerator = loader ? loader : addImageSizeParametersToUrl;\n  return setSizes\n    .map(\n      (size) =>\n        `${srcGenerator({\n          src,\n          width: size,\n          // height is not applied if there is no crop\n          // if there is crop, then height is applied as a ratio of the original width + height aspect ratio * size\n          height: crop ? Number(size) * aspectRatio : undefined,\n          crop,\n          scale,\n        })} ${size}w`\n    )\n    .join(', ');\n}\n"],"names":["Image","data","width","height","loading","loader","shopifyImageLoader","loaderOptions","widths","decoding","rest","url","missingUrlError","id","Error","altText","alt","console","warn","imgElementWidth","imgElementHeight","getShopifyImageDimensions","elementProps","finalSrc","src","maxWidth","finalSrcset","srcSet","internalImageSrcSet","undefined","crop","scale","hasCustomWidths","Array","isArray","some","size","isNaN","aspectRatio","Number","setSizes","IMG_SRC_SET_SIZES","length","filter","srcGenerator","addImageSizeParametersToUrl","map","join"],"mappings":"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"}