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

Chrome: Adding the featured image panel #894

Merged
merged 3 commits into from
May 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions components/spinner/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const spinner = <span className="spinner is-active" />;
Copy link
Member

Choose a reason for hiding this comment

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

Nice optimization 👍 (related)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have a good teacher :)


export default () => spinner;
140 changes: 140 additions & 0 deletions editor/sidebar/featured-image/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/**
* External dependencies
*/
import { connect } from 'react-redux';

/**
* WordPress dependencies
*/
import { Component } from 'element';
import { __ } from 'i18n';
import Button from 'components/button';
import PanelBody from 'components/panel/body';
import MediaUploadButton from 'blocks/media-upload-button';
import Spinner from 'components/spinner';

/**
* Internal dependencies
*/
import './style.scss';
import { getEditedPostAttribute } from '../../selectors';
import { editPost } from '../../actions';

class FeaturedImage extends Component {
constructor() {
super( ...arguments );
this.state = {
media: null,
loading: false,
};
}

componentDidMount() {
this.fetchMedia();
}

componentDidUpdate( prevProps ) {
if ( prevProps.featuredImageId !== this.props.featuredImageId ) {
this.fetchMedia();
}
}

componentWillUnmount() {
if ( this.fetchMediaRequest ) {
this.fetchMediaRequest.abort();
}
}

fetchMedia() {
this.setState( { media: null } );
if ( ! this.props.featuredImageId ) {
this.setState( { loading: false } );
return;
}
this.setState( { loading: true } );
const mediaIdToLoad = this.props.featuredImageId;
this.fetchMediaRequest = new wp.api.models.Media( { id: mediaIdToLoad } ).fetch()
.done( ( media ) => {
if ( this.props.featuredImageId !== mediaIdToLoad ) {
return;
}
this.setState( {
loading: false,
media,
} );
} )
.fail( () => {
if ( this.props.featuredImageId !== mediaIdToLoad ) {
return;
}
this.setState( {
loading: false,
} );
} );
}

render() {
const { featuredImageId, onUpdateImage, onRemoveImage } = this.props;
const { media, loading } = this.state;

return (
<PanelBody title={ __( 'Featured image' ) } initialOpen={ false }>
<div className="editor-featured-image__content">
{ !! featuredImageId &&
<MediaUploadButton
buttonProps={ { className: 'button-link' } }
onSelect={ onUpdateImage }
type="image"
>
{ media &&
<img
Copy link
Member

Choose a reason for hiding this comment

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

One minor thing we might consider is using the media's dimensions to assign a height to the space that the image will occupy, to avoid the jarring appearance of content being pushed down only after the image has loaded.

Height is difficult to calculate unless we know the width of the container, which we probably don't want to assume. We could use a technique for proportionally scaled responsive containers though, using padding to dynamically fill height.

See:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure I understand. The height depends on the loaded image. So even if we know the width container, we don't know the ratio of the loaded image, how can we compute the height in this case?

Copy link
Member

Choose a reason for hiding this comment

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

To your point, more accurately there's two resources that need to be loaded:

  • The media entity REST API resource
  • The image itself

The idea is that after the first of these finishes loading, we know the dimensions of the image and the space we expect it to consume, even before the image itself has finished loading.

Practically speaking this is probably not very noticeable though, since either we've loaded a post with a featured image already assigned and Settings -> Featured Image is likely collapsed, or we've just assigned the image from the media library and the image is already cached by the browser.

Copy link
Member

@aduth aduth May 26, 2017

Choose a reason for hiding this comment

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

Here's maybe a clearer demonstration of the issue/solution and its particular relevance when we don't know the containing width:

https://codepen.io/aduth/pen/jmRGpV

className="editor-featured-image__preview"
src={ media.source_url }
alt={ __( 'Featured image' ) }
/>
}
{ loading && <Spinner /> }
</MediaUploadButton>
}
{ !! featuredImageId && media &&
<p className="editor-featured-image__howto">
{ __( 'Click the image to edit or update' ) }
</p>
}
{ ! featuredImageId &&
<MediaUploadButton
buttonProps={ { className: 'editor-featured-image__toggle button-link' } }
onSelect={ onUpdateImage }
type="image"
>
{ wp.i18n.__( 'Set featured image' ) }
</MediaUploadButton>
}
{ !! featuredImageId &&
<Button className="editor-featured-image__toggle button-link" onClick={ onRemoveImage }>
{ wp.i18n.__( 'Remove featured image' ) }
</Button>
}
</div>
</PanelBody>
);
}
}

export default connect(
( state ) => {
return {
featuredImageId: getEditedPostAttribute( state, 'featured_media' ),
};
},
( dispatch ) => {
return {
onUpdateImage( image ) {
dispatch( editPost( { featured_media: image.id } ) );
},
onRemoveImage() {
dispatch( editPost( { featured_media: null } ) );
},
};
}
)( FeaturedImage );
32 changes: 32 additions & 0 deletions editor/sidebar/featured-image/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.editor-featured-image__content {
padding: 10px 0 0;

.spinner {
margin: 0;
}
}

.editor-featured-image__toggle {
text-decoration: underline;
color: $blue-wordpress;

&:focus {
box-shadow: none;
outline: none;
}

&:hover {
color: $blue-medium;
}
}

.editor-featured-image__preview {
display: block;
max-width: 100%;
}

.editor-featured-image__howto {
color: $dark-gray-300;
font-style: italic;
margin: 10px 0;
}
2 changes: 2 additions & 0 deletions editor/sidebar/post-settings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import IconButton from 'components/icon-button';
import './style.scss';
import PostStatus from '../post-status';
import PostExcerpt from '../post-excerpt';
import FeaturedImage from '../featured-image';

const PostSettings = ( { toggleSidebar } ) => {
return (
Expand All @@ -32,6 +33,7 @@ const PostSettings = ( { toggleSidebar } ) => {
</PanelHeader>
<PostStatus />
<PostExcerpt />
<FeaturedImage />
</Panel>
);
};
Expand Down