UNPKG

@wordpress/components

Version:
275 lines (253 loc) 6.51 kB
/** * External dependencies */ import { Image as RNImage, Text, View } from 'react-native'; import FastImage from 'react-native-fast-image'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { Icon } from '@wordpress/components'; import { image as icon } from '@wordpress/icons'; import { usePreferredColorSchemeStyle } from '@wordpress/compose'; import { useEffect, useState, Platform } from '@wordpress/element'; /** * Internal dependencies */ import { getImageWithFocalPointStyles } from './utils'; import styles from './style.scss'; import SvgIconRetry from './icon-retry'; import ImageEditingButton from './image-editing-button'; const ICON_TYPE = { PLACEHOLDER: 'placeholder', RETRY: 'retry', UPLOAD: 'upload', }; export const IMAGE_DEFAULT_FOCAL_POINT = { x: 0.5, y: 0.5 }; const ImageComponent = ( { align, alt, editButton = true, focalPoint, height: imageHeight, highlightSelected = true, isSelected, shouldUseFastImage, isUploadFailed, isUploadInProgress, mediaPickerOptions, onImageDataLoad, onSelectMediaUploadOption, openMediaOptions, resizeMode, retryMessage, retryIcon, url, shapeStyle, style, width: imageWidth, } ) => { const [ imageData, setImageData ] = useState( null ); const [ containerSize, setContainerSize ] = useState( null ); // Disabled for Android due to https://github.com/WordPress/gutenberg/issues/43149 const Image = ! shouldUseFastImage || Platform.isAndroid ? RNImage : FastImage; const imageResizeMode = ! shouldUseFastImage || Platform.isAndroid ? resizeMode : FastImage.resizeMode[ resizeMode ]; useEffect( () => { let isCurrent = true; if ( url ) { RNImage.getSize( url, ( imgWidth, imgHeight ) => { if ( ! isCurrent ) { return; } const metaData = { aspectRatio: imgWidth / imgHeight, width: imgWidth, height: imgHeight, }; setImageData( metaData ); if ( onImageDataLoad ) { onImageDataLoad( metaData ); } } ); } return () => ( isCurrent = false ); // Disable reason: deferring this refactor to the native team. // see https://github.com/WordPress/gutenberg/pull/41166 // eslint-disable-next-line react-hooks/exhaustive-deps }, [ url ] ); const onContainerLayout = ( event ) => { const { height, width } = event.nativeEvent.layout; if ( width !== 0 && height !== 0 && ( containerSize?.width !== width || containerSize?.height !== height ) ) { setContainerSize( { width, height } ); } }; const getIcon = ( iconType ) => { let iconStyle; switch ( iconType ) { case ICON_TYPE.RETRY: return ( <Icon icon={ retryIcon || SvgIconRetry } { ...styles.iconRetry } /> ); case ICON_TYPE.PLACEHOLDER: iconStyle = iconPlaceholderStyles; break; case ICON_TYPE.UPLOAD: iconStyle = iconUploadStyles; break; } return <Icon icon={ icon } { ...iconStyle } />; }; const iconPlaceholderStyles = usePreferredColorSchemeStyle( styles.iconPlaceholder, styles.iconPlaceholderDark ); const iconUploadStyles = usePreferredColorSchemeStyle( styles.iconUpload, styles.iconUploadDark ); const placeholderStyles = [ usePreferredColorSchemeStyle( styles.imageContainerUpload, styles.imageContainerUploadDark ), focalPoint && styles.imageContainerUploadWithFocalpoint, imageHeight && { height: imageHeight }, ]; const customWidth = imageData?.width < containerSize?.width ? imageData?.width : styles.wide?.width; const imageContainerStyles = [ styles.imageContent, { width: imageWidth === styles.wide?.width || ( imageData && imageWidth > 0 && imageWidth < containerSize?.width ) ? imageWidth : customWidth, }, resizeMode && { width: styles.wide?.width }, focalPoint && styles.focalPointContainer, ]; const imageStyles = [ { opacity: isUploadInProgress ? 0.3 : 1, height: containerSize?.height, }, ! resizeMode && { aspectRatio: imageData?.aspectRatio, }, focalPoint && styles.focalPoint, focalPoint && getImageWithFocalPointStyles( focalPoint, containerSize, imageData ), ! focalPoint && imageData && containerSize && { height: imageData?.width > containerSize?.width ? containerSize?.width / imageData?.aspectRatio : undefined, }, imageHeight && { height: imageHeight }, shapeStyle, ]; return ( <View style={ [ styles.container, // Only set alignItems if an image exists because alignItems causes the placeholder // to disappear when an aligned image can't be downloaded // https://github.com/wordpress-mobile/gutenberg-mobile/issues/1592 imageData && align && { alignItems: align }, style, ] } onLayout={ onContainerLayout } > <View accessible disabled={ ! isSelected } accessibilityLabel={ alt } accessibilityHint={ __( 'Double tap and hold to edit' ) } accessibilityRole={ 'imagebutton' } key={ url } style={ imageContainerStyles } > { isSelected && highlightSelected && ! ( isUploadInProgress || isUploadFailed ) && ( <View style={ [ styles.imageBorder, { height: containerSize?.height }, ] } /> ) } { ! imageData ? ( <View style={ placeholderStyles }> <View style={ styles.imageUploadingIconContainer }> { getIcon( ICON_TYPE.UPLOAD ) } </View> </View> ) : ( <View style={ focalPoint && styles.focalPointContent }> <Image style={ imageStyles } source={ { uri: url } } { ...( ! focalPoint && { resizeMethod: 'scale', } ) } resizeMode={ imageResizeMode } /> </View> ) } { isUploadFailed && retryMessage && ( <View style={ [ styles.imageContainer, styles.retryContainer, ] } > <View style={ [ styles.retryIcon, retryIcon && styles.customRetryIcon, ] } > { getIcon( ICON_TYPE.RETRY ) } </View> <Text style={ styles.uploadFailedText }> { retryMessage } </Text> </View> ) } </View> { editButton && isSelected && ! isUploadInProgress && ( <ImageEditingButton onSelectMediaUploadOption={ onSelectMediaUploadOption } openMediaOptions={ openMediaOptions } url={ ! isUploadFailed && imageData && url } pickerOptions={ mediaPickerOptions } /> ) } </View> ); }; export default ImageComponent;