diff --git a/packages/block-editor/src/components/index.native.js b/packages/block-editor/src/components/index.native.js index b2c91bc8655db9..07b298c6baa53b 100644 --- a/packages/block-editor/src/components/index.native.js +++ b/packages/block-editor/src/components/index.native.js @@ -28,7 +28,6 @@ export { MEDIA_TYPE_VIDEO, } from './media-upload'; export { default as MediaUploadProgress } from './media-upload-progress'; -export { default as MediaEdit } from './media-edit'; export { default as URLInput } from './url-input'; export { default as BlockInvalidWarning } from './block-list/block-invalid-warning'; export { default as BlockCaption } from './block-caption'; diff --git a/packages/block-editor/src/components/media-upload-progress/README.md b/packages/block-editor/src/components/media-upload-progress/README.md index 89ef9243717774..9447ac4d065c68 100644 --- a/packages/block-editor/src/components/media-upload-progress/README.md +++ b/packages/block-editor/src/components/media-upload-progress/README.md @@ -13,17 +13,13 @@ import { MediaUploadProgress, } from '@wordpress/block-editor'; -function MediaProgress( { height, width, url, id } ) { +function MediaProgress( { url, id } ) { return ( { + renderContent={ ( { isUploadFailed, retryMessage } ) => { return ( @@ -50,14 +46,6 @@ A media ID that identifies the current upload. - Required: Yes - Platform: Mobile -### coverUrl - -By passing an image url, it'll calculate the right size depending on the container of the component maintaining its aspect ratio, it'll pass these values through `renderContent`. - -- Type: `String` -- Required: No -- Platform: Mobile - ### renderContent Content to be rendered along with the progress bar, usually the thumbnail of the media being uploaded. @@ -68,31 +56,8 @@ Content to be rendered along with the progress bar, usually the thumbnail of the It passes an object containing the following properties: -**With** `coverUrl` as a parameter: - -`{ isUploadInProgress, isUploadFailed, finalWidth, finalHeight, imageWidthWithinContainer, retryMessage }` - -**Without** `coverUrl` as a parameter: - `{ isUploadInProgress, isUploadFailed, retryMessage }` - -### width - -Forces the final width to be returned in `finalWidth` - -- Type: `Number` -- Required: No -- Platform: Mobile - -### height - -Forces the final height to be returned in `finalHeight` - -- Type: `Number` -- Required: No -- Platform: Mobile - ### onUpdateMediaProgress Callback called when the progress of the upload is updated. diff --git a/packages/block-editor/src/components/media-upload-progress/image-size.native.js b/packages/block-editor/src/components/media-upload-progress/image-size.native.js deleted file mode 100644 index 3e022777fce2cc..00000000000000 --- a/packages/block-editor/src/components/media-upload-progress/image-size.native.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * WordPress dependencies - */ -import { Component } from '@wordpress/element'; - -/** - * External dependencies - */ -import { View, Image } from 'react-native'; - -/** - * Internal dependencies - */ - -function calculatePreferedImageSize( image, container ) { - const maxWidth = container.clientWidth; - const exceedMaxWidth = image.width > maxWidth; - const ratio = image.height / image.width; - const width = exceedMaxWidth ? maxWidth : image.width; - const height = exceedMaxWidth ? maxWidth * ratio : image.height; - return { width, height }; -} - -class ImageSize extends Component { - constructor() { - super( ...arguments ); - this.state = { - width: undefined, - height: undefined, - }; - this.onLayout = this.onLayout.bind( this ); - } - - componentDidUpdate( prevProps ) { - if ( this.props.src !== prevProps.src ) { - this.image = {}; - - this.setState( { - width: undefined, - height: undefined, - } ); - this.fetchImageSize(); - } - - if ( this.props.dirtynessTrigger !== prevProps.dirtynessTrigger ) { - this.calculateSize(); - } - } - - componentDidMount() { - this.fetchImageSize(); - } - - fetchImageSize() { - Image.getSize( this.props.src, ( width, height ) => { - this.image = { width, height }; - this.calculateSize(); - } ); - } - - calculateSize() { - if ( this.image === undefined || this.container === undefined ) { - return; - } - const { width, height } = calculatePreferedImageSize( - this.image, - this.container - ); - this.setState( { width, height } ); - } - - onLayout( event ) { - const { width, height } = event.nativeEvent.layout; - this.container = { - clientWidth: width, - clientHeight: height, - }; - this.calculateSize(); - } - - render() { - const sizes = { - imageWidth: this.image && this.image.width, - imageHeight: this.image && this.image.height, - containerWidth: this.container && this.container.width, - containerHeight: this.container && this.container.height, - imageWidthWithinContainer: this.state.width, - imageHeightWithinContainer: this.state.height, - }; - return ( - - { this.props.children( sizes ) } - - ); - } -} - -export default ImageSize; diff --git a/packages/block-editor/src/components/media-upload-progress/index.native.js b/packages/block-editor/src/components/media-upload-progress/index.native.js index 7f2f7d380ad32d..adf99249384103 100644 --- a/packages/block-editor/src/components/media-upload-progress/index.native.js +++ b/packages/block-editor/src/components/media-upload-progress/index.native.js @@ -14,7 +14,6 @@ import { subscribeMediaUpload } from '@wordpress/react-native-bridge'; /** * Internal dependencies */ -import ImageSize from './image-size'; import styles from './styles.scss'; export const MEDIA_UPLOAD_STATE_UPLOADING = 1; @@ -117,12 +116,7 @@ export class MediaUploadProgress extends React.Component { } render() { - const { - coverUrl, - width, - height, - renderContent = () => {}, - } = this.props; + const { renderContent = () => null } = this.props; const { isUploadInProgress, isUploadFailed } = this.state; const showSpinner = this.state.isUploadInProgress; const progress = this.state.progress * 100; @@ -138,46 +132,11 @@ export class MediaUploadProgress extends React.Component { ) } - { coverUrl && ( - - { ( sizes ) => { - const { - imageWidthWithinContainer, - imageHeightWithinContainer, - } = sizes; - - let finalHeight = imageHeightWithinContainer; - if ( - height > 0 && - height < imageHeightWithinContainer - ) { - finalHeight = height; - } - - let finalWidth = imageWidthWithinContainer; - if ( - width > 0 && - width < imageWidthWithinContainer - ) { - finalWidth = width; - } - return renderContent( { - isUploadInProgress, - isUploadFailed, - finalWidth, - finalHeight, - imageWidthWithinContainer, - retryMessage, - } ); - } } - - ) } - { ! coverUrl && - renderContent( { - isUploadInProgress, - isUploadFailed, - retryMessage, - } ) } + { renderContent( { + isUploadInProgress, + isUploadFailed, + retryMessage, + } ) } ); } diff --git a/packages/block-editor/src/components/media-upload-progress/test/index.native.js b/packages/block-editor/src/components/media-upload-progress/test/index.native.js index 030fba6b37a20f..a9c9adc6eaf54c 100644 --- a/packages/block-editor/src/components/media-upload-progress/test/index.native.js +++ b/packages/block-editor/src/components/media-upload-progress/test/index.native.js @@ -26,9 +26,15 @@ jest.mock( '@wordpress/react-native-bridge', () => { const subscribeMediaUpload = ( callback ) => { this.uploadCallBack = callback; }; + const mediaSources = { + deviceCamera: 'DEVICE_CAMERA', + deviceLibrary: 'DEVICE_MEDIA_LIBRARY', + siteMediaLibrary: 'SITE_MEDIA_LIBRARY', + }; return { subscribeMediaUpload, sendMediaUpload: callUploadCallback, + mediaSources, }; } ); diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index e0ecc3ed7cb073..91314ea668db3d 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -2,13 +2,9 @@ * External dependencies */ import React from 'react'; -import { - View, - ImageBackground, - Text, - TouchableWithoutFeedback, - Dimensions, -} from 'react-native'; +import { View, TouchableWithoutFeedback } from 'react-native'; +import { isEmpty, get, find, map } from 'lodash'; + /** * WordPress dependencies */ @@ -19,11 +15,6 @@ import { requestImageUploadCancelDialog, requestImageFullscreenPreview, } from '@wordpress/react-native-bridge'; -import { isEmpty, get, find, map } from 'lodash'; - -/** - * WordPress dependencies - */ import { CycleSelectControl, Icon, @@ -32,6 +23,7 @@ import { ToggleControl, ToolbarButton, ToolbarGroup, + Image, } from '@wordpress/components'; import { BlockCaption, @@ -42,7 +34,6 @@ import { BlockControls, InspectorControls, BlockAlignmentToolbar, - MediaEdit, } from '@wordpress/block-editor'; import { __, sprintf } from '@wordpress/i18n'; import { getProtocol } from '@wordpress/url'; @@ -52,7 +43,7 @@ import { withSelect } from '@wordpress/data'; import { external, link, - image as icon, + image as placeholderIcon, textColor, replace, } from '@wordpress/icons'; @@ -61,21 +52,10 @@ import { * Internal dependencies */ import styles from './styles.scss'; -import SvgIconRetry from './icon-retry'; -import SvgIconCustomize from './icon-customize'; import { getUpdatedLinkTargetSettings } from './utils'; import { LINK_DESTINATION_CUSTOM, DEFAULT_SIZE_SLUG } from './constants'; -const ICON_TYPE = { - PLACEHOLDER: 'placeholder', - RETRY: 'retry', - UPLOAD: 'upload', -}; - -// Default Image ratio 4:3 -const IMAGE_ASPECT_RATIO = 4 / 3; - const getUrlForSlug = ( image, { sizeSlug } ) => { return get( image, [ 'media_details', 'sizes', sizeSlug, 'source_url' ] ); }; @@ -107,6 +87,9 @@ export class ImageEdit extends React.Component { this.onImagePressed = this.onImagePressed.bind( this ); this.onFocusCaption = this.onFocusCaption.bind( this ); this.updateAlignment = this.updateAlignment.bind( this ); + this.accessibilityLabelCreator = this.accessibilityLabelCreator.bind( + this + ); } componentDidMount() { @@ -173,6 +156,17 @@ export class ImageEdit extends React.Component { }; } + accessibilityLabelCreator( caption ) { + return isEmpty( caption ) + ? /* translators: accessibility text. Empty image caption. */ + 'Image caption. Empty' + : sprintf( + /* translators: accessibility text. %s: image caption. */ + __( 'Image caption. %s' ), + caption + ); + } + onImagePressed() { const { attributes, image } = this.props; @@ -313,33 +307,24 @@ export class ImageEdit extends React.Component { } } - getIcon( iconType ) { - let iconStyle; - switch ( iconType ) { - case ICON_TYPE.RETRY: - return ; - case ICON_TYPE.PLACEHOLDER: - iconStyle = this.props.getStylesFromColorScheme( + getPlaceholderIcon() { + return ( + ; + ) } + /> + ); } render() { + const { isCaptionSelected } = this.state; const { attributes, isSelected, image, imageSizes } = this.props; const { align, url, - height, width, alt, href, @@ -416,11 +401,11 @@ export class ImageEdit extends React.Component { if ( ! url ) { return ( - + @@ -435,197 +420,71 @@ export class ImageEdit extends React.Component { wide: 'center', }; - const imageContainerHeight = - Dimensions.get( 'window' ).width / IMAGE_ASPECT_RATIO; - - const editImageComponent = ( { open, mediaOptions } ) => ( - - - - { mediaOptions() } - ( + <> + + + { getInspectorControls() } + { getMediaOptions() } + { ! this.state.isCaptionSelected && + getToolbarEditButton( openMediaOptions ) } + { + return ( + { + ); + } } /> - - - ); - - const getImageComponent = ( openMediaOptions, getMediaOptions ) => ( - - - { getInspectorControls() } - { getMediaOptions() } - { ! this.state.isCaptionSelected && - getToolbarEditButton( openMediaOptions ) } - { - const opacity = isUploadInProgress ? 0.3 : 1; - const imageBorderOnSelectedStyle = - isSelected && - ! ( - isUploadInProgress || - isUploadFailed || - this.state.isCaptionSelected - ) - ? styles.imageBorder - : ''; - - const iconRetryContainer = ( - - { this.getIcon( ICON_TYPE.RETRY ) } - - ); - - return ( - - { ! imageWidthWithinContainer && ( - - - { this.getIcon( - ICON_TYPE.UPLOAD - ) } - - - ) } - - { isUploadFailed && ( - - { iconRetryContainer } - - { retryMessage } - - - ) } - { isSelected && - ! isUploadInProgress && - ! isUploadFailed && - finalWidth && - finalHeight && ( - - ) } - - - ); - } } - /> - - isEmpty( caption ) - ? /* translators: accessibility text. Empty image caption. */ - 'Image caption. Empty' - : sprintf( - /* translators: accessibility text. %s: image caption. */ - __( 'Image caption. %s' ), - caption - ) - } - onFocus={ this.onFocusCaption } - onBlur={ this.props.onBlur } // always assign onBlur as props - insertBlocksAfter={ this.props.insertBlocksAfter } - /> - - + + + ); return ( diff --git a/packages/block-library/src/image/styles.native.scss b/packages/block-library/src/image/styles.native.scss index d4c512ea423cdd..3dc1e439710914 100644 --- a/packages/block-library/src/image/styles.native.scss +++ b/packages/block-library/src/image/styles.native.scss @@ -1,49 +1,15 @@ // @format -.imageContainer { - justify-content: center; - align-items: center; - background-color: $gray-lighten-30; -} - -.imageBorder { - border-color: $blue-medium; - border-width: 2px; - border-style: solid; -} - -.uploadFailedText { - color: #fff; - font-size: 14; - margin-top: 5; -} - -.uploadFailedContainer { - position: absolute; - flex-direction: column; - align-items: center; -} - -.modalIcon { - width: 80px; - height: 80px; - justify-content: center; - align-items: center; +.content { + flex: 1; } -.imageUploadingIconContainer { - width: 40px; - height: 40px; +.contentCentered { + flex: 1; justify-content: center; align-items: center; } -.iconRetry { - fill: #fff; - width: 100%; - height: 100%; -} - .iconPlaceholder { fill: $gray-dark; width: 100%; @@ -53,58 +19,3 @@ .iconPlaceholderDark { fill: $white; } - -.iconUpload { - fill: $gray-lighten-20; - width: 100%; - height: 100%; -} - -.iconUploadDark { - fill: $gray-70; -} - -.imageContainerUpload { - justify-content: center; - align-items: center; - background-color: $gray-light; -} - -.imageContainerUploadDark { - background-color: $gray-90; -} - -.content { - flex: 1; -} - -.contentCentered { - flex: 1; - justify-content: center; - align-items: center; -} - -.editContainer { - width: 44px; - height: 44px; - position: absolute; - top: 0; - right: 0; -} - -.edit { - width: 30px; - height: 30px; - background-color: $gray-dark; - border-radius: 22px; - position: absolute; - top: 5px; - right: 5px; -} - -.iconCustomise { - fill: #fff; - position: absolute; - top: 7px; - left: 7px; -} diff --git a/packages/block-library/src/media-text/edit.native.js b/packages/block-library/src/media-text/edit.native.js index a8ef35c775ba1a..3eaab4e09988fb 100644 --- a/packages/block-library/src/media-text/edit.native.js +++ b/packages/block-library/src/media-text/edit.native.js @@ -12,13 +12,21 @@ import { BlockControls, BlockVerticalAlignmentToolbar, InnerBlocks, + InspectorControls, withColors, + MEDIA_TYPE_IMAGE, + MEDIA_TYPE_VIDEO, } from '@wordpress/block-editor'; import { Component } from '@wordpress/element'; -import { ToolbarGroup } from '@wordpress/components'; +import { + Button, + ToolbarGroup, + PanelBody, + ToggleControl, +} from '@wordpress/components'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { pullLeft, pullRight } from '@wordpress/icons'; +import { pullLeft, pullRight, replace } from '@wordpress/icons'; /** * Internal dependencies @@ -47,10 +55,24 @@ class MediaTextEdit extends Component { this.onWidthChange = this.onWidthChange.bind( this ); this.commitWidthChange = this.commitWidthChange.bind( this ); this.onLayoutChange = this.onLayoutChange.bind( this ); + this.onMediaSelected = this.onMediaSelected.bind( this ); + this.onReplaceMedia = this.onReplaceMedia.bind( this ); + this.onSetOpenPickerRef = this.onSetOpenPickerRef.bind( this ); + this.onSetImageFill = this.onSetImageFill.bind( this ); this.state = { mediaWidth: null, containerWidth: 0, + isMediaSelected: false, + }; + } + + static getDerivedStateFromProps( props, state ) { + return { + isMediaSelected: + state.isMediaSelected && + props.isSelected && + ! props.isAncestorSelected, }; } @@ -134,7 +156,47 @@ class MediaTextEdit extends Component { } ); } - renderMediaArea() { + onMediaSelected() { + this.setState( { isMediaSelected: true } ); + } + + onReplaceMedia() { + if ( this.openPickerRef ) { + this.openPickerRef(); + } + } + + onSetOpenPickerRef( openPicker ) { + this.openPickerRef = openPicker; + } + + onSetImageFill() { + const { attributes, setAttributes } = this.props; + const { imageFill } = attributes; + + setAttributes( { + imageFill: ! imageFill, + } ); + } + + getControls() { + const { attributes } = this.props; + const { imageFill } = attributes; + + return ( + + + + + + ); + } + renderMediaArea( shouldStack ) { + const { isMediaSelected, containerWidth } = this.state; const { attributes, isSelected } = this.props; const { mediaAlt, @@ -145,25 +207,40 @@ class MediaTextEdit extends Component { mediaWidth, imageFill, focalPoint, + verticalAlignment, } = attributes; + const mediaAreaWidth = + mediaWidth && ! shouldStack + ? ( containerWidth * mediaWidth ) / 100 - + styles.mediaAreaPadding.width + : containerWidth; + const aligmentStyles = + styles[ + `is-vertically-aligned-${ verticalAlignment || 'center' }` + ]; return ( ); @@ -180,11 +257,13 @@ class MediaTextEdit extends Component { } = this.props; const { isStackedOnMobile, + imageFill, mediaPosition, mediaWidth, + mediaType, verticalAlignment, } = attributes; - const { containerWidth } = this.state; + const { containerWidth, isMediaSelected } = this.state; const isMobile = containerWidth < BREAKPOINTS.mobile; const shouldStack = isStackedOnMobile && isMobile; @@ -216,20 +295,26 @@ class MediaTextEdit extends Component { ...( isSelected && styles[ 'is-selected' ] ), backgroundColor: wrapperProps?.style?.backgroundColor || backgroundColor.color, + paddingBottom: 0, }; const innerBlockWidth = shouldStack ? 100 : 100 - temporaryMediaWidth; const innerBlockWidthString = `${ innerBlockWidth }%`; - const mediaContainerStyle = shouldStack - ? { - ...( mediaPosition === 'left' && styles.mediaStackLeft ), - ...( mediaPosition === 'right' && styles.mediaStackRight ), - } - : { - ...( mediaPosition === 'left' && styles.mediaLeft ), - ...( mediaPosition === 'right' && styles.mediaRight ), - }; + const mediaContainerStyle = [ + { flex: 1 }, + shouldStack + ? { + ...( mediaPosition === 'left' && + styles.mediaStackLeft ), + ...( mediaPosition === 'right' && + styles.mediaStackRight ), + } + : { + ...( mediaPosition === 'left' && styles.mediaLeft ), + ...( mediaPosition === 'right' && styles.mediaRight ), + }, + ]; const toolbarControls = [ { @@ -252,21 +337,41 @@ class MediaTextEdit extends Component { return ( <> + { mediaType === MEDIA_TYPE_IMAGE && this.getControls() } - - + { ( isMediaSelected || mediaType === MEDIA_TYPE_VIDEO ) && ( + +