11/*
2- * Copyright (c) 2021, salesforce.com, inc .
2+ * Copyright (c) 2025, Salesforce, Inc .
33 * All rights reserved.
44 * SPDX-License-Identifier: BSD-3-Clause
55 * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66 */
77import React , { useMemo } from 'react'
8+ import { Helmet } from 'react-helmet'
89import PropTypes from 'prop-types'
910import { Box , useTheme } from '@salesforce/retail-react-app/app/components/shared/ui'
10- import Image from '@salesforce/retail-react-app/app/components/image'
11- import { getResponsiveImageAttributes } from '@salesforce/retail-react-app/app/utils/responsive-image'
11+ import { Img } from '@salesforce/retail-react-app/app/components/shared/ui'
12+ import { getResponsivePictureAttributes } from '@salesforce/retail-react-app/app/utils/responsive-image'
13+ import {
14+ getImageAttributes ,
15+ getImageLinkAttributes
16+ } from '@salesforce/retail-react-app/app/utils/image'
17+ import { isServer } from '@salesforce/retail-react-app/app/components/image/utils'
1218
1319/**
14- * Quickly create a responsive image using your Dynamic Imaging Service
15- * @example
16- * // Widths without a unit are interpreted as px values
17- * <DynamicImage src="http://example.com/image.jpg[?sw={width}&q=60]" widths={[100, 360, 720]} />
18- * <DynamicImage src="http://example.com/image.jpg[?sw={width}&q=60]" widths={{base: 100, sm: 360, md: 720}} />
19- * // You can also use units of px or vw
20- * <DynamicImage src="http://example.com/image.jpg[?sw={width}&q=60]" widths={['50vw', '100vw', '500px']} />
20+ * Responsive image component optimized to work with the Dynamic Imaging Service.
21+ * Via this component it's easy to create a `<picture>` element with related
22+ * theme-aware `<source>` elements and responsive preloading for high-priority
23+ * images.
24+ * @example Widths without a unit defined as array (interpreted as px values)
25+ * <DynamicImage
26+ * src="http://example.com/image.jpg[?sw={width}&q=60]"
27+ * widths={[100, 360, 720]} />
28+ * @example Widths without a unit defined as object (interpreted as px values)
29+ * <DynamicImage
30+ * src="http://example.com/image.jpg[?sw={width}&q=60]"
31+ * widths={{base: 100, sm: 360, md: 720}} />
32+ * @example Widths with mixed px and vw units defined as array
33+ * <DynamicImage
34+ * src="http://example.com/image.jpg[?sw={width}&q=60]"
35+ * widths={['50vw', '100vw', '500px']} />
36+ * @example Eagerly load image with high priority and responsive preloading
37+ * <DynamicImage
38+ * src="http://example.com/image.jpg[?sw={width}&q=60]"
39+ * widths={['50vw', '50vw', '20vw', '20vw', '25vw']}
40+ * imageProps={{loading: 'eager'}}
41+ * />
42+ * @see {@link https://web.dev/learn/design/responsive-images }
43+ * @see {@link https://web.dev/learn/design/picture-element }
44+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/picture }
45+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Responsive_images }
2146 * @see {@link https://help.salesforce.com/s/articleView?id=cc.b2c_image_transformation_service.htm&type=5 }
2247 */
2348const DynamicImage = ( { src, widths, densities, imageProps, as, ...rest } ) => {
24- const Component = as ? as : Image
49+ const Component = as ? as : Img
2550 const theme = useTheme ( )
2651
27- const responsiveImageProps = useMemo (
28- ( ) =>
29- getResponsiveImageAttributes ( { src, widths, densities, breakpoints : theme . breakpoints } ) ,
30- [ src , widths , densities , theme . breakpoints ]
31- )
52+ const [ responsiveImageProps , numSources , effectiveImageProps , responsiveLinks ] = useMemo ( ( ) => {
53+ const responsiveImageProps = getResponsivePictureAttributes ( {
54+ src,
55+ widths,
56+ densities,
57+ breakpoints : theme . breakpoints
58+ } )
59+ const effectiveImageProps = getImageAttributes ( imageProps )
60+ const fetchPriority = effectiveImageProps . fetchPriority
61+ const responsiveLinks =
62+ ! responsiveImageProps . links . length && fetchPriority === 'high'
63+ ? [
64+ getImageLinkAttributes ( {
65+ ...effectiveImageProps ,
66+ fetchPriority, // React <18 vs. >=19 issue
67+ src : responsiveImageProps . src
68+ } )
69+ ]
70+ : responsiveImageProps . links . reduce ( ( acc , link ) => {
71+ const linkProps = getImageLinkAttributes ( {
72+ ...effectiveImageProps ,
73+ ...link ,
74+ fetchPriority, // React <18 vs. >=19 issue
75+ src : responsiveImageProps . src
76+ } )
77+ if ( linkProps ) {
78+ acc . push ( linkProps )
79+ }
80+ return acc
81+ } , [ ] )
82+ return [
83+ responsiveImageProps ,
84+ responsiveImageProps . sources . length ,
85+ effectiveImageProps ,
86+ responsiveLinks
87+ ]
88+ } , [ src , widths , densities , theme . breakpoints ] )
3289
3390 return (
3491 < Box { ...rest } >
35- < Component { ...responsiveImageProps } { ...imageProps } />
92+ { numSources > 0 ? (
93+ < picture >
94+ { responsiveImageProps . sources . map ( ( { srcSet, sizes, media} , idx ) => {
95+ if ( idx < numSources - 1 ) {
96+ return < source key = { idx } media = { media } sizes = { sizes } srcSet = { srcSet } />
97+ }
98+ return (
99+ < Component
100+ key = { idx }
101+ { ...effectiveImageProps }
102+ sizes = { sizes }
103+ srcSet = { srcSet }
104+ src = { responsiveImageProps . src }
105+ />
106+ )
107+ } ) }
108+ </ picture >
109+ ) : (
110+ < Component { ...effectiveImageProps } src = { responsiveImageProps . src } />
111+ ) }
112+
113+ { isServer ( ) && responsiveLinks . length > 0 && (
114+ < Helmet >
115+ { responsiveLinks . map ( ( responsiveLinkProps , idx ) => {
116+ const { href, ...rest } = responsiveLinkProps
117+ return < link key = { idx } { ...rest } href = { href } />
118+ } ) }
119+ </ Helmet >
120+ ) }
36121 </ Box >
37122 )
38123}
@@ -49,7 +134,7 @@ DynamicImage.propTypes = {
49134 /**
50135 * Image density factors to apply relative to the breakpoints. Will be mapped to the corresponding `srcSet`.
51136 */
52- densities : PropTypes . array ,
137+ densities : PropTypes . oneOfType ( [ PropTypes . array , PropTypes . object ] ) ,
53138 /**
54139 * Props to pass to the inner image component
55140 */
0 commit comments