-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathLazyImages.tsx
More file actions
112 lines (98 loc) · 2.95 KB
/
Copy pathLazyImages.tsx
File metadata and controls
112 lines (98 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import React, { useContext, FC } from 'react'
import styles from './LazyImages.css'
import { Helmet } from 'react-helmet'
interface LazyImagesContext {
lazyLoad: boolean
/** "native" uses the attribute "loading=lazy", which is good but only works
* on Chrome. lazysizes is a JS plugin already provided by render-runtime,
* with broader support, but the behaviour might be a little worse at times */
method: 'native' | 'lazysizes'
}
const LazyImagesContext = React.createContext<LazyImagesContext>({
lazyLoad: false,
method: 'lazysizes',
})
interface LazyImagesProps {
lazyLoad?: boolean
experimentalMethod?: LazyImagesContext['method']
}
const LazyImages: FC<LazyImagesProps> = ({
children,
lazyLoad = true,
experimentalMethod = 'lazysizes',
}) => {
return (
<LazyImagesContext.Provider
value={{ lazyLoad, method: experimentalMethod }}
>
{children}
</LazyImagesContext.Provider>
)
}
const useLazyImagesContext = () => {
const value = useContext(LazyImagesContext)
return value
}
interface MaybeLazyImageProps {
createElement: typeof React.createElement
imageProps: Record<string, any>
loadingType: string
}
const MaybeLazyImage: FC<MaybeLazyImageProps> = ({
createElement = React.createElement,
imageProps,
loadingType,
}) => {
const { lazyLoad, method } = useLazyImagesContext()
if (lazyLoad && loadingType !== 'eager') {
let newImageProps = imageProps
switch (method) {
case 'native':
newImageProps = {
...imageProps,
loading: 'lazy',
}
break
case 'lazysizes':
/** adds `lazyload` class to enable lazysizes, and moves the image URI
* from src to data-src. `styles.lazyload` is used to hide the "broken image"
* symbol while the image hasn't been loaded */
newImageProps = {
...imageProps,
// explicity key because we need react to re-render the whole img element
// in case the src changes (i.e: product-summary + sku)
key: imageProps.src,
className: `lazyload ${imageProps.className ?? ''} ${
styles.lazyload
}`,
src: undefined,
'data-src': imageProps.src,
loading: 'lazy',
}
break
}
return createElement.apply(React, ['img', newImageProps])
}
const isPreloaded = String(imageProps['data-vtex-preload']) === 'true'
// If it's preloaded, render the image along with a Helmet that adds preload
if (isPreloaded) {
return (
<>
<Helmet
link={[
{
rel: 'preload',
as: 'image',
href: imageProps.src,
crossOrigin: imageProps.crossOrigin,
},
]}
/>
{createElement.apply(React, ['img', imageProps])}
</>
)
}
// Otherwise, just render the image
return createElement.apply(React, ['img', imageProps])
}
export { LazyImages, MaybeLazyImage, useLazyImagesContext }