-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathPicture.js
120 lines (100 loc) · 3.18 KB
/
Picture.js
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
113
114
115
116
117
118
119
120
const React = require('react')
const h = React.createElement
const Picture = React.forwardRef(function Picture({ src, sizes, breakpoints, ...imgProps }, ref) {
if (!src) {
return null
}
if (src.default) {
src = src.default
}
if (!Array.isArray(src)) {
src = [src]
}
if (!Array.isArray(sizes)) {
sizes = [sizes]
}
if (src.length === 0) {
return null
}
// fallback img is for old browsers, which
// we're mostly not that interested in, but do it
// as part of the <picture /> spec
const fallbackImg = src[0]
// each image has breakdpoints config attached
// from the plugin config, use that but only
// if it's not overriden via props
breakpoints = breakpoints || src[0].breakpoints
const flattendSrc = flattenSrc(src, sizes, breakpoints)
// when we have only one image source (that is we're not doing
// art direction), provide the width and height for the image,
// so that the browser knows the aspect ratio of the images
const dimensions =
src.length === 1
? {
width: src[0].images[0].width,
height: src[0].images[0].height,
}
: {}
return h(
'picture',
{},
flattendSrc.map(({ img, sizes, breakpoints, maxWidth }, i) =>
h(
React.Fragment,
{ key: i },
img.webpSrcSet &&
img.webpSrcSet.length > 0 &&
h('source', {
type: 'image/webp',
srcSet: img.webpSrcSet,
sizes: makeSizes(img, sizes, breakpoints),
...(maxWidth ? { media: `(max-width: ${maxWidth}px)` } : {}),
}),
img.srcSet &&
img.srcSet.length > 0 &&
h('source', {
type: img.type,
srcSet: img.srcSet,
sizes: makeSizes(img, sizes, breakpoints),
...(maxWidth ? { media: `(max-width: ${maxWidth}px)` } : {}),
}),
),
),
h('img', { ...imgProps, ref, src: fallbackImg.src, srcSet: fallbackImg.srcSet, ...dimensions }),
)
})
function flattenSrc(src, sizes, breakpoints) {
// console.log(src, sizes, breakpoints)
const result = []
for (let i = 0; i < src.length; i++) {
if (i > breakpoints.length) {
console.warn(`Image ${src[i].name} was not included due to insufficient breakpoints.`)
continue
}
result.push({
img: src[i],
sizes: sizes[i],
breakpoints: src.length === 1 ? breakpoints : [],
maxWidth: i === src.length - 1 ? null : breakpoints[i],
})
}
return result
}
function makeSizes(img, sizes, breakpoints) {
// a custom sizes value has been provided via props for this image
if (sizes) return sizes
// otherwise, we generate a sizes definition using the metadata
// about available image sizes and breakpoints provided by the loader
return img.sizes
.reduce((acc, s, i) => {
if (i > breakpoints.length) {
console.warn(`The ${img.name} @ ${s}w will never be shown due to insufficient breakpoints.`)
return acc
}
return breakpoints[i] && img.sizes.length - 1 > i
? acc.concat(`(max-width: ${breakpoints[i]}px) ${s}px`)
: acc.concat(`${s}px`)
}, [])
.join(', ')
}
module.exports = { Picture, makeSizes, flattenSrc }