import React from 'react' import {ComponentPropsWithoutRef, ReactNode} from 'react' import {createSrcSet} from './helpers' export const srcErrorMsg = `src must be a valid Cloudinary URL. To embed a local image, use a regular img tag instead of this ResponsiveImage component.` /** * @internal */ export const VALID_CLOUDINARY_START = `https://res.cloudinary.com` /** * @category Props */ export type ResponsiveImageProps = ComponentPropsWithoutRef<`img`> & { fallback?: JSX.Element alt?: string } /** * This component renders a lazy-loading, responsive image served from Cloudinary. * * @remarks * This component renders a lazy-loading, responsive image, with `srcset` generated and served from Cloudinary, and `sizes` attribute automatically generated at runtime so that only the most suitable image is loaded by the client. * * It handles all of this complexity plus optimisation concerns. The only thing you need to do is provide a valid Cloudinary URL as the `src` prop. It will not work with locally hosted images. Please use a regular `img` tag for those (with the `src` attribute prepended with $\{root\}). * * @param props - The attributes of a regular `img` element plus an optional `fallback` element. {@link ResponsiveImageProps}. However, note that the `src` attribute must be a valid Cloudinary URL. * @returns a lazy-loading, optimised, responsive img element. * * @category Components * * @example * ### Basic usage * ```jsx * import {Img} from '@myadbox/exoplanet-toolkit' * // later, in the template JSX... * cat being funny * ``` * * ### Style customisation * You may pass in `className` and `style` props to be applied to the underlying `img` element. The default `className` is set to be `width:100% height:auto` (which are [Hucssley](https://github.com/stowball/hucssley) utility classes). Passing any other value will override this, so you will need to apply your own sizing classes. * * Also, note that `object-fit:cover` is applied to the `className` by default (even if overriding the above-mentioned default), but you can override that by passing in an alternative `object-fit` class with your custom `className`.. * ```jsx * import {Img} from '@myadbox/exoplanet-toolkit' * // later, in the template JSX... * cat being funny * ``` * * ### With fallback * You can also provide a fallback element to be rendered when the image is not available. * ```jsx * import {Img} from '@myadbox/exoplanet-toolkit' * // later, in the template JSX... * cat being funny * } * /> * ``` * * @see createSrcSet * @see injectTransforms */ export const ResponsiveImage = ({ src = ``, // TODO: portraitSrc = src, // TODO: landscapeSrc = src, className = `width:100% height:auto w-full h-auto`, fallback = null, alt = ``, ...props }: ResponsiveImageProps): JSX.Element => { if (!src || typeof src !== `string` || !src.startsWith(VALID_CLOUDINARY_START)) { console.warn(srcErrorMsg) return fallback } const {src: fallbackSrc, srcSet} = createSrcSet(src) return ( {alt} ) } export default ResponsiveImage