import type { Property } from 'csstype' ; import * as React from 'react' ; import { ColorValue , ImageProps as BaseImageProps , ImageResizeMode , ImageStyle , ImageURISource as BaseImageURISource , StyleProp , StyleSheet , View , ViewStyle , } from 'react-native' ; export interface ResponsiveImageSource { see https readonly srcSet : string ; see https readonly type : string ; see https readonly media ? : string | null ; } export interface ImageURISource extends BaseImageURISource { /**
     * Responsive image sources.
     */ sources ? : readonly ResponsiveImageSource [ ] | null ; } export type ImageSourcePropType = ImageURISource | readonly ImageURISource [ ] | number ; const interopDefault = ( source ) : ImageURISource => typeof source . default === 'object' ? source . default : source ; const resolveSource = ( source ) : { uri : string | undefined ; sources : readonly ResponsiveImageSource [ ] | null | undefined ; width : number | undefined ; height : number | undefined ; } => { if ( source == null || typeof source === 'string' ) { return { uri : source , sources : undefined , width : undefined , height : undefined , } ; } else if ( typeof source === 'object' ) { const sourceObject = interopDefault ( source ) ; const width = typeof sourceObject . width === 'number' ? sourceObject . width : undefined ; const height = typeof sourceObject . height === 'number' ? sourceObject . height : undefined ; return { uri : sourceObject . uri , sources : sourceObject . sources , width , height , } ; } else { return { uri : undefined , sources : undefined , width : undefined , height : undefined , } ; } } ; const resizeModeToObjectFit = ( resizeMode ) : Property . ObjectFit => { switch ( resizeMode ) { case 'cover' : return 'cover' ; case 'contain' : return 'contain' ; case 'stretch' : return 'fill' ; case 'center' : return 'none' ; default : throw new Error ( 'Unsupported resize mode: ' + resizeMode ) ; } } ; let _filterId = 0 ; const createTintColorSVG = ( tintColor , id ) => { return tintColor != null && id != null ? ( < svg style = { { position : 'absolute' , height : 0 , visibility : 'hidden' , width : 0 , } } > < defs > { } < filter id = { `tint-${ id }` } suppressHydrationWarning = { true } > < feFlood floodColor = { `${ tintColor as string }` } key = { tintColor as string } / > < feComposite in2 = "SourceAlpha" operator = "atop" / > < /filter> < /defs> < /svg>) : null; } ; const getFlatStyle = ( style , blurRadius , propsTintColor , filterId ) : [ ViewStyle , ImageStyle [ 'resizeMode' ] , Property . Filter | undefined , ColorValue | undefined ] => { const flatStyle = { ... StyleSheet . flatten ( style ) } ; const { filter , resizeMode } = flatStyle as any ; const tintColor = flatStyle . tintColor ?? propsTintColor ; const filters = [ ] ; let _filter ; if ( filter ) { filters . push ( filter ) ; } if ( blurRadius ) { filters . push ( `blur(${ blurRadius }px)` ) ; } if ( tintColor && filterId != null ) { filters . push ( `url(#tint-${ filterId })` ) ; } if ( filters . length > 0 ) { _filter = filters . join ( ' ' ) ; } delete flatStyle . tintColor ; delete flatStyle . overlayColor ; delete flatStyle . resizeMode ; return [ flatStyle , resizeMode , _filter , tintColor ] ; } ; export interface ImageProps extends Omit < BaseImageProps , 'source' > { platform critical ? : boolean ; platform draggable ? : boolean ; source : ImageSourcePropType ; tintColor ? : ColorValue ; } export const Image = React . forwardRef < View , ImageProps > ( ( { source , resizeMode : propsResizeMode , accessibilityLabel , width , height , critical , style , draggable , tintColor : propsTintColor , blurRadius , fadeDuration = 300 , onLoadEnd , ... others } , ref ) => { const filterRef = React . useRef ( _filterId ++ ) ; const resolvedSource = resolveSource ( source ) ; const [ flatStyle , _resizeMode , filter , tintColor ] = getFlatStyle ( style , blurRadius , propsTintColor , filterRef . current ) ; const resizeMode = propsResizeMode ?? _resizeMode ; const [ loaded , setLoaded ] = React . useState < boolean | null > ( fadeDuration === 0 ? true : null ) ; const imgRef = React . useRef < HTMLImageElement | null > ( null ) ; const timeoutRef = React . useRef < any > ( null ) ; React . useEffect ( ( ) => { if ( fadeDuration !== 0 ) { timeoutRef . current = setTimeout ( ( ) => { if ( imgRef . current != null && ! imgRef . current . complete ) { setLoaded ( false ) ; } } , 50 ) ; } return ( ) => { clearTimeout ( timeoutRef . current ) ; } ; } , [ ] ) ; const hasCalledInitialLoad = React . useRef ( false ) ; React . useEffect ( ( ) => { if ( imgRef . current != null && imgRef . current . complete && ! hasCalledInitialLoad . current ) { onLoadEnd ?. ( ) ; } } , [ ] ) ; const onLoad = ( ) => { hasCalledInitialLoad . current = true ; clearTimeout ( timeoutRef . current ) ; setLoaded ( true ) ; onLoadEnd ?. ( ) ; } ; return ( < View ref = { ref } style = { [ { overflow : 'hidden' , width : width === undefined && resolvedSource . width != null ? resolvedSource . width : width , height , } , flatStyle , ] } { ... others } > { resolvedSource . width != null && resolvedSource . height != null && ( < div style = { { paddingTop : `${ ( resolvedSource . height / resolvedSource . width ) * 100 }%` , } } />)} { source != null && ( < picture style = { { position : 'absolute' , top : 0 , left : 0 , right : 0 , bottom : 0 , } } > { resolvedSource . sources ?. map ( ( s , i ) => ( < source key = { i } srcSet = { s . srcSet } media = { s . media } sizes = { s . sizes } type = { s . type } / > ) ) } < img ref = { imgRef } style = { { width : '100%' , height : '100%' , filter , objectFit : resizeModeToObjectFit ( resizeMode ?? ( resolvedSource . width != null && width == null ? 'contain' : 'cover' ) ) , opacity : loaded === false ? 0 : 1 , transition : loaded != null && fadeDuration !== 0 ? `opacity ${ fadeDuration }ms linear` : undefined , } } src = { resolvedSource . uri } loading = { critical ? 'eager' : 'lazy' } alt = { accessibilityLabel } width = { resolvedSource . width } height = { resolvedSource . height } draggable = { draggable } onLoad = { onLoad } / > < /picture>)} { createTintColorSVG ( tintColor , filterRef . current ) } < /View>); } ) ;