@wordpress/block-library
Version:
Block library for the WordPress editor.
271 lines (253 loc) • 7.49 kB
JavaScript
/**
* WordPress dependencies
*/
import { getBlobByURL, isBlobURL } from '@wordpress/blob';
import {
BaseControl,
Button,
Disabled,
IconButton,
PanelBody,
SelectControl,
ToggleControl,
Toolbar,
withNotices,
} from '@wordpress/components';
import {
BlockControls,
BlockIcon,
InspectorControls,
MediaPlaceholder,
MediaUpload,
MediaUploadCheck,
RichText,
} from '@wordpress/block-editor';
import { mediaUpload } from '@wordpress/editor';
import { Component, Fragment, createRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { createUpgradedEmbedBlock } from '../embed/util';
import icon from './icon';
const ALLOWED_MEDIA_TYPES = [ 'video' ];
const VIDEO_POSTER_ALLOWED_MEDIA_TYPES = [ 'image' ];
class VideoEdit extends Component {
constructor() {
super( ...arguments );
// edit component has its own src in the state so it can be edited
// without setting the actual value outside of the edit UI
this.state = {
editing: ! this.props.attributes.src,
};
this.videoPlayer = createRef();
this.posterImageButton = createRef();
this.toggleAttribute = this.toggleAttribute.bind( this );
this.onSelectURL = this.onSelectURL.bind( this );
this.onSelectPoster = this.onSelectPoster.bind( this );
this.onRemovePoster = this.onRemovePoster.bind( this );
}
componentDidMount() {
const { attributes, noticeOperations, setAttributes } = this.props;
const { id, src = '' } = attributes;
if ( ! id && isBlobURL( src ) ) {
const file = getBlobByURL( src );
if ( file ) {
mediaUpload( {
filesList: [ file ],
onFileChange: ( [ { url } ] ) => {
setAttributes( { src: url } );
},
onError: ( message ) => {
this.setState( { editing: true } );
noticeOperations.createErrorNotice( message );
},
allowedTypes: ALLOWED_MEDIA_TYPES,
} );
}
}
}
componentDidUpdate( prevProps ) {
if ( this.props.attributes.poster !== prevProps.attributes.poster ) {
this.videoPlayer.current.load();
}
}
toggleAttribute( attribute ) {
return ( newValue ) => {
this.props.setAttributes( { [ attribute ]: newValue } );
};
}
onSelectURL( newSrc ) {
const { attributes, setAttributes } = this.props;
const { src } = attributes;
// Set the block's src from the edit component's state, and switch off
// the editing UI.
if ( newSrc !== src ) {
// Check if there's an embed block that handles this URL.
const embedBlock = createUpgradedEmbedBlock(
{ attributes: { url: newSrc } }
);
if ( undefined !== embedBlock ) {
this.props.onReplace( embedBlock );
return;
}
setAttributes( { src: newSrc, id: undefined } );
}
this.setState( { editing: false } );
}
onSelectPoster( image ) {
const { setAttributes } = this.props;
setAttributes( { poster: image.url } );
}
onRemovePoster() {
const { setAttributes } = this.props;
setAttributes( { poster: '' } );
// Move focus back to the Media Upload button.
this.posterImageButton.current.focus();
}
render() {
const {
autoplay,
caption,
controls,
loop,
muted,
poster,
preload,
src,
} = this.props.attributes;
const { setAttributes, isSelected, className, noticeOperations, noticeUI } = this.props;
const { editing } = this.state;
const switchToEditing = () => {
this.setState( { editing: true } );
};
const onSelectVideo = ( media ) => {
if ( ! media || ! media.url ) {
// in this case there was an error and we should continue in the editing state
// previous attributes should be removed because they may be temporary blob urls
setAttributes( { src: undefined, id: undefined } );
switchToEditing();
return;
}
// sets the block's attribute and updates the edit component from the
// selected media, then switches off the editing UI
setAttributes( { src: media.url, id: media.id } );
this.setState( { src: media.url, editing: false } );
};
if ( editing ) {
return (
<MediaPlaceholder
icon={ <BlockIcon icon={ icon } /> }
className={ className }
onSelect={ onSelectVideo }
onSelectURL={ this.onSelectURL }
accept="video/*"
allowedTypes={ ALLOWED_MEDIA_TYPES }
value={ this.props.attributes }
notices={ noticeUI }
onError={ noticeOperations.createErrorNotice }
/>
);
}
/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
return (
<Fragment>
<BlockControls>
<Toolbar>
<IconButton
className="components-icon-button components-toolbar__control"
label={ __( 'Edit video' ) }
onClick={ switchToEditing }
icon="edit"
/>
</Toolbar>
</BlockControls>
<InspectorControls>
<PanelBody title={ __( 'Video Settings' ) }>
<ToggleControl
label={ __( 'Autoplay' ) }
onChange={ this.toggleAttribute( 'autoplay' ) }
checked={ autoplay }
/>
<ToggleControl
label={ __( 'Loop' ) }
onChange={ this.toggleAttribute( 'loop' ) }
checked={ loop }
/>
<ToggleControl
label={ __( 'Muted' ) }
onChange={ this.toggleAttribute( 'muted' ) }
checked={ muted }
/>
<ToggleControl
label={ __( 'Playback Controls' ) }
onChange={ this.toggleAttribute( 'controls' ) }
checked={ controls }
/>
<SelectControl
label={ __( 'Preload' ) }
value={ preload }
onChange={ ( value ) => setAttributes( { preload: value } ) }
options={ [
{ value: 'auto', label: __( 'Auto' ) },
{ value: 'metadata', label: __( 'Metadata' ) },
{ value: 'none', label: __( 'None' ) },
] }
/>
<MediaUploadCheck>
<BaseControl
className="editor-video-poster-control"
label={ __( 'Poster Image' ) }
>
<MediaUpload
title={ __( 'Select Poster Image' ) }
onSelect={ this.onSelectPoster }
allowedTypes={ VIDEO_POSTER_ALLOWED_MEDIA_TYPES }
render={ ( { open } ) => (
<Button
isDefault
onClick={ open }
ref={ this.posterImageButton }
>
{ ! this.props.attributes.poster ? __( 'Select Poster Image' ) : __( 'Replace image' ) }
</Button>
) }
/>
{ !! this.props.attributes.poster &&
<Button onClick={ this.onRemovePoster } isLink isDestructive>
{ __( 'Remove Poster Image' ) }
</Button>
}
</BaseControl>
</MediaUploadCheck>
</PanelBody>
</InspectorControls>
<figure className={ className }>
{ /*
Disable the video tag so the user clicking on it won't play the
video when the controls are enabled.
*/ }
<Disabled>
<video
controls={ controls }
poster={ poster }
src={ src }
ref={ this.videoPlayer }
/>
</Disabled>
{ ( ! RichText.isEmpty( caption ) || isSelected ) && (
<RichText
tagName="figcaption"
placeholder={ __( 'Write caption…' ) }
value={ caption }
onChange={ ( value ) => setAttributes( { caption: value } ) }
inlineToolbar
/>
) }
</figure>
</Fragment>
);
/* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
}
}
export default withNotices( VideoEdit );