@@ -16,15 +16,19 @@ declare namespace ContentfulImageTransform {
1616 | 'faces'
1717 type Format = 'jpg' | 'progressive' | 'gif' | 'png' | '8bit' | 'webp' | 'avif'
1818 type Fit = 'pad' | 'fill' | 'scale' | 'crop' | 'thumb'
19+ type Key = 'q' | 'w' | 'h' | 'fit' | 'f' | 'r' | 'fm' | 'fl' | 'bg'
1920}
2021
21- type MediaQuery = Record < number , {
22+ export type MediaQuery = {
2223 width ?: number ,
2324 height ?: number ,
24- } >
25+ quality ?: number
26+ }
27+
28+ export type MediaQueries = Record < string , MediaQuery >
2529
2630type Props = {
27- image : TypeImage // Asset['fields']['file']
31+ image : TypeImage
2832 alt ?: string
2933 width ?: number ,
3034 height ?: number ,
@@ -35,19 +39,47 @@ type Props = {
3539 focusArea ?: ContentfulImageTransform . FocusArea ,
3640 radius ?: number
3741 decoding ?: 'auto' | 'sync' | 'async' ,
38- // to be implemented
39- // mediaQueries?: MediaQuery
42+ mediaQueries ?: MediaQueries
4043} ;
4144
42- const imagePropsMap = {
43- width : 'w' ,
44- height : 'h' ,
45- behaviour : 'fit' ,
46- quality : 'q' ,
47- backgroundColor : 'bg' ,
48- focusArea : 'f' ,
49- radius : 'r' ,
50- format : 'fm'
45+ function buildQueryParam ( key : ContentfulImageTransform . Key , value ?: string | number ) : Record < string , string > | null {
46+ if ( value ) {
47+ return { [ key ] : value . toString ( ) }
48+ } else {
49+ return null ;
50+ }
51+ }
52+
53+ function buildFormatQueryParam ( format ?: ContentfulImageTransform . Format ) : Record < string , string > | null {
54+ if ( ! format ) {
55+ return null
56+ } else {
57+ if ( format === '8bit' ) {
58+ return { fm : 'png' , fl : 'png8' }
59+ } else if ( format === 'progressive' ) {
60+ return { fm : 'jpg' , fl : 'progressive' }
61+ } else {
62+ return { fm : format }
63+ }
64+ }
65+ }
66+
67+ function buildSrcUrl ( url : string , query : Record < string , string > ) {
68+ return `${ url } ?${ new URLSearchParams ( query ) . toString ( ) } `
69+ }
70+
71+ function buildMimeTime ( format ?: ContentfulImageTransform . Format ) {
72+ if ( ! format ) {
73+ return
74+ }
75+ switch ( format ) {
76+ case '8bit' :
77+ return 'image/png'
78+ case 'progressive' :
79+ return 'image/jpeg'
80+ default :
81+ return `image/${ format || 'jpeg' } `
82+ }
5183}
5284
5385const ContentfulImage : React . FC < Props > = (
@@ -62,71 +94,47 @@ const ContentfulImage: React.FC<Props> = (
6294 focusArea,
6395 format,
6496 radius,
65- decoding = 'auto'
97+ decoding = 'auto' ,
98+ mediaQueries = { }
6699 } ) => {
67100
68- let mimeType = image . contentType // extract mimeType from image.url
69- let query = { } ;
70-
71- // should only allow ContentfulImage.Query props
72- function addToQuery ( prop : Record < string , string > ) {
73- query = { ...query , ...prop }
74- }
75-
76- if ( quality ) {
77- addToQuery ( { [ imagePropsMap [ 'quality' ] ] : quality . toString ( ) } )
101+ let mimeType = buildMimeTime ( format ) || image . contentType
102+
103+ const query = {
104+ ...buildQueryParam ( 'q' , quality ) ,
105+ ...buildQueryParam ( 'w' , width ) ,
106+ ...buildQueryParam ( 'h' , height ) ,
107+ ...buildQueryParam ( 'fit' , behaviour ) ,
108+ ...buildQueryParam ( 'f' , focusArea ) ,
109+ ...buildQueryParam ( 'r' , radius ) ,
110+ ...buildQueryParam ( 'bg' , backgroundColor ) ,
111+ ...buildFormatQueryParam ( format ) ,
78112 }
79113
80- if ( width ) {
81- addToQuery ( { [ imagePropsMap [ 'width' ] ] : width . toString ( ) } )
82- }
83-
84- if ( height ) {
85- addToQuery ( { [ imagePropsMap [ 'height' ] ] : height . toString ( ) } )
86- }
87-
88- if ( behaviour ) {
89- addToQuery ( { [ imagePropsMap [ 'behaviour' ] ] : behaviour . toString ( ) } )
90- }
91-
92- if ( focusArea ) {
93- addToQuery ( { [ imagePropsMap [ 'focusArea' ] ] : focusArea . toString ( ) } )
94- }
95-
96- if ( radius ) {
97- addToQuery ( { [ imagePropsMap [ 'radius' ] ] : radius . toString ( ) } )
98- }
99-
100- if ( format ) {
101- if ( format === '8bit' ) {
102- addToQuery ( { 'fm' : 'png' } )
103- addToQuery ( { 'fl' : 'png8' } )
104- mimeType = 'image/png'
105- } else if ( format === 'progressive' ) {
106- addToQuery ( { 'fm' : 'jpg' } )
107- addToQuery ( { 'fl' : 'progressive' } )
108- mimeType = 'image/jpg'
109- } else {
110- addToQuery ( { [ imagePropsMap [ 'format' ] ] : format . toString ( ) } )
111- mimeType = `image/${ format } `
114+ const mediaQuerySources = Object . keys ( mediaQueries ) . map ( ( key : string ) => {
115+ const sourceQuery = {
116+ ...query ,
117+ ...buildQueryParam ( 'w' , mediaQueries [ key ] . width ) ,
118+ ...buildQueryParam ( 'h' , mediaQueries [ key ] . height ) ,
112119 }
113- }
114-
115- if ( quality ) {
116- addToQuery ( { [ imagePropsMap [ 'quality' ] ] : quality . toString ( ) } )
117- }
118-
119- if ( backgroundColor ) {
120- addToQuery ( { [ imagePropsMap [ 'backgroundColor' ] ] : backgroundColor . toString ( ) } )
121- }
120+ return (
121+ < source
122+ srcSet = { buildSrcUrl ( image . url , sourceQuery ) }
123+ type = { ` ${ mimeType } ` }
124+ media = { key }
125+ key = { key }
126+ />
127+ )
128+ } )
122129
123- const transformSrc = ` ${ image . url } ? ${ new URLSearchParams ( query ) . toString ( ) } `
130+ const src = buildSrcUrl ( image . url , query ) ;
124131
125132 return (
126133 < picture >
127- < source srcSet = { transformSrc } type = { `${ mimeType } ` } />
134+ { mediaQuerySources }
135+ < source srcSet = { src } type = { `${ mimeType } ` } />
128136 < img
129- src = { image . url }
137+ src = { src }
130138 alt = { alt || image . title }
131139 decoding = { decoding }
132140 width = { width }
0 commit comments