Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mobile] Image height #13096

Merged
merged 10 commits into from
Jan 16, 2019
129 changes: 83 additions & 46 deletions packages/block-library/src/image/edit.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,67 +9,104 @@ import RNReactNativeGutenbergBridge from 'react-native-gutenberg-bridge';
*/
import { MediaPlaceholder, RichText, BlockControls } from '@wordpress/editor';
import { Toolbar, ToolbarButton } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import ImageSize from './image-size';

export default function ImageEdit( props ) {
const { attributes, isSelected, setAttributes } = props;
const { url, caption } = attributes;
class ImageEdit extends Component {
constructor() {
super( ...arguments );
this.onMediaLibraryPress = this.onMediaLibraryPress.bind( this );
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Tug ! I had to add this bind back since it was giving an error accessing this.props.setAttributes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, usually onSomething methods need to be bound to this 👍

}

const onUploadPress = () => {
onUploadPress() {
// This method should present an image picker from
// the device.
//TODO: Implement upload image method.
};
}

const onMediaLibraryPress = () => {
onMediaLibraryPress() {
RNReactNativeGutenbergBridge.onMediaLibraryPress( ( mediaUrl ) => {
if ( mediaUrl ) {
setAttributes( { url: mediaUrl } );
this.props.setAttributes( { url: mediaUrl } );
}
} );
};
}

if ( ! url ) {
toolbarEditButton() {
etoledom marked this conversation as resolved.
Show resolved Hide resolved
return (
<MediaPlaceholder
onUploadPress={ onUploadPress }
onMediaLibraryPress={ onMediaLibraryPress }
/>
<Toolbar>
<ToolbarButton
className="components-toolbar__control"
label={ __( 'Edit image' ) }
icon="edit"
onClick={ this.onMediaLibraryPress }
/>
</Toolbar>
);
}

const toolbarEditButton = (
<Toolbar>
<ToolbarButton
className="components-toolbar__control"
label={ __( 'Edit image' ) }
icon="edit"
onClick={ onMediaLibraryPress }
/>
</Toolbar>
);
render() {
const { attributes, isSelected, setAttributes } = this.props;
const { url, caption, height, width } = attributes;

if ( ! url ) {
return (
<MediaPlaceholder
onUploadPress={ this.onUploadPress }
onMediaLibraryPress={ this.onMediaLibraryPress }
/>
);
}

return (
<View style={ { flex: 1 } }>
<BlockControls>
{ this.toolbarEditButton() }
</BlockControls>
<ImageSize src={ url } >
{ ( sizes ) => {
const {
imageWidthWithinContainer,
imageHeightWithinContainer,
} = sizes;

return (
<View style={ { flex: 1 } }>
<BlockControls>
{ toolbarEditButton }
</BlockControls>
<Image
style={ { width: '100%', height: 200 } }
resizeMethod="scale"
source={ { uri: url } }
/>
{ ( ! RichText.isEmpty( caption ) > 0 || isSelected ) && (
<View style={ { padding: 12, flex: 1 } }>
<TextInput
style={ { textAlign: 'center' } }
underlineColorAndroid="transparent"
value={ caption }
placeholder={ 'Write caption…' }
onChangeText={ ( newCaption ) => setAttributes( { caption: newCaption } ) }
/>
</View>
) }
</View>
);
let finalHeight = imageHeightWithinContainer;
if ( height > 0 && height < imageHeightWithinContainer ) {
finalHeight = height;
}

let finalWidth = imageWidthWithinContainer;
if ( width > 0 && width < imageWidthWithinContainer ) {
finalWidth = width;
}

return (
<View style={ { flex: 1 } } >
<Image
style={ { width: finalWidth, height: finalHeight } }
resizeMethod="scale"
source={ { uri: url } }
key={ url }
/>
</View>
);
} }
</ImageSize>
{ ( ! RichText.isEmpty( caption ) > 0 || isSelected ) && (
<View style={ { padding: 12, flex: 1 } }>
<TextInput
style={ { textAlign: 'center' } }
underlineColorAndroid="transparent"
value={ caption }
placeholder={ 'Write caption…' }
onChangeText={ ( newCaption ) => setAttributes( { caption: newCaption } ) }
/>
</View>
) }
</View>
);
}
}

export default ImageEdit;
11 changes: 6 additions & 5 deletions packages/block-library/src/image/image-size.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import { noop } from 'lodash';
import { withGlobalEvents } from '@wordpress/compose';
import { Component } from '@wordpress/element';

/**
* Internal dependencies
*/
import { calculatePreferedImageSize } from './utils';

class ImageSize extends Component {
constructor() {
super( ...arguments );
Expand Down Expand Up @@ -55,11 +60,7 @@ class ImageSize extends Component {
}

calculateSize() {
const maxWidth = this.container.clientWidth;
const exceedMaxWidth = this.image.width > maxWidth;
const ratio = this.image.height / this.image.width;
const width = exceedMaxWidth ? maxWidth : this.image.width;
const height = exceedMaxWidth ? maxWidth * ratio : this.image.height;
const { width, height } = calculatePreferedImageSize( this.image, this.container );
this.setState( { width, height } );
}

Expand Down
87 changes: 87 additions & 0 deletions packages/block-library/src/image/image-size.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';

/**
* External dependencies
*/
import { View, Image } from 'react-native';

/**
* Internal dependencies
*/
import { calculatePreferedImageSize } from './utils';

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 (
<View onLayout={ this.onLayout }>
{ this.props.children( sizes ) }
</View>
);
}
}

export default ImageSize;
9 changes: 9 additions & 0 deletions packages/block-library/src/image/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

export function calculatePreferedImageSize( image, container ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add some JSDocs :)

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 };
}