1import React, { useEffect, Ref, useMemo } from 'react'; 2 3import ColorTintFilter, { getTintColorStyle } from './ColorTintFilter'; 4import { ImageWrapperProps } from './ImageWrapper.types'; 5import { getImageWrapperEventHandler } from './getImageWrapperEventHandler'; 6import { absoluteFilledPosition, getObjectPositionFromContentPositionObject } from './positioning'; 7import { SrcSetSource } from './useSourceSelection'; 8import { ImageNativeProps, ImageSource } from '../Image.types'; 9import { useBlurhash } from '../utils/blurhash/useBlurhash'; 10import { isBlurhashString, isThumbhashString } from '../utils/resolveSources'; 11import { thumbHashStringToDataURL } from '../utils/thumbhash/thumbhash'; 12 13function getFetchPriorityFromImagePriority(priority: ImageNativeProps['priority'] = 'normal') { 14 return priority && ['low', 'high'].includes(priority) ? priority : 'auto'; 15} 16 17function getImgPropsFromSource(source: ImageSource | SrcSetSource | null | undefined) { 18 if (source && 'srcset' in source) { 19 return { 20 srcSet: source.srcset, 21 sizes: source.sizes, 22 }; 23 } 24 return {}; 25} 26 27const ImageWrapper = React.forwardRef( 28 ( 29 { 30 source, 31 events, 32 contentPosition, 33 hashPlaceholderContentPosition, 34 priority, 35 style, 36 hashPlaceholderStyle, 37 tintColor, 38 className, 39 accessibilityLabel, 40 ...props 41 }: ImageWrapperProps, 42 ref: Ref<HTMLImageElement> 43 ) => { 44 useEffect(() => { 45 events?.onMount?.forEach((e) => e?.()); 46 }, []); 47 48 const isBlurhash = isBlurhashString(source?.uri || ''); 49 const isThumbhash = isThumbhashString(source?.uri || ''); 50 const isHash = isBlurhash || isThumbhash; 51 52 // Thumbhash uri always has to start with 'thumbhash:/' 53 const thumbhash = source?.uri?.replace(/thumbhash:\//, ''); 54 const thumbhashUri = useMemo( 55 () => (isThumbhash ? thumbHashStringToDataURL(thumbhash ?? '') : null), 56 [thumbhash] 57 ); 58 59 const blurhashUri = useBlurhash(isBlurhash ? source?.uri : null, source?.width, source?.height); 60 if (!source) { 61 return null; 62 } 63 64 const objectPosition = getObjectPositionFromContentPositionObject( 65 isHash ? hashPlaceholderContentPosition : contentPosition 66 ); 67 68 const uri = isHash ? blurhashUri ?? thumbhashUri : source?.uri; 69 70 return ( 71 <> 72 <ColorTintFilter tintColor={tintColor} /> 73 <img 74 ref={ref} 75 alt={accessibilityLabel} 76 className={className} 77 src={uri || undefined} 78 key={source?.uri} 79 style={{ 80 objectPosition, 81 ...absoluteFilledPosition, 82 ...getTintColorStyle(tintColor), 83 ...(isHash ? hashPlaceholderStyle : {}), 84 ...style, 85 }} 86 // @ts-ignore 87 // eslint-disable-next-line react/no-unknown-property 88 fetchpriority={getFetchPriorityFromImagePriority(priority || 'normal')} 89 {...getImageWrapperEventHandler(events, source)} 90 {...getImgPropsFromSource(source)} 91 {...props} 92 /> 93 </> 94 ); 95 } 96); 97 98export default ImageWrapper; 99