UNPKG

5.73 kBJavaScriptView Raw
1/**
2 * WordPress dependencies
3 */
4import { getBlobByURL, isBlobURL } from '@wordpress/blob';
5import {
6 Disabled,
7 IconButton,
8 PanelBody,
9 SelectControl,
10 ToggleControl,
11 Toolbar,
12 withNotices,
13} from '@wordpress/components';
14import {
15 BlockControls,
16 BlockIcon,
17 InspectorControls,
18 MediaPlaceholder,
19 RichText,
20} from '@wordpress/block-editor';
21import { mediaUpload } from '@wordpress/editor';
22import { Component, Fragment } from '@wordpress/element';
23import { __ } from '@wordpress/i18n';
24
25/**
26 * Internal dependencies
27 */
28import icon from './icon';
29
30/**
31 * Internal dependencies
32 */
33import { createUpgradedEmbedBlock } from '../embed/util';
34
35const ALLOWED_MEDIA_TYPES = [ 'audio' ];
36
37class AudioEdit extends Component {
38 constructor() {
39 super( ...arguments );
40 // edit component has its own src in the state so it can be edited
41 // without setting the actual value outside of the edit UI
42 this.state = {
43 editing: ! this.props.attributes.src,
44 };
45
46 this.toggleAttribute = this.toggleAttribute.bind( this );
47 this.onSelectURL = this.onSelectURL.bind( this );
48 }
49
50 componentDidMount() {
51 const { attributes, noticeOperations, setAttributes } = this.props;
52 const { id, src = '' } = attributes;
53
54 if ( ! id && isBlobURL( src ) ) {
55 const file = getBlobByURL( src );
56
57 if ( file ) {
58 mediaUpload( {
59 filesList: [ file ],
60 onFileChange: ( [ { id: mediaId, url } ] ) => {
61 setAttributes( { id: mediaId, src: url } );
62 },
63 onError: ( e ) => {
64 setAttributes( { src: undefined, id: undefined } );
65 this.setState( { editing: true } );
66 noticeOperations.createErrorNotice( e );
67 },
68 allowedTypes: ALLOWED_MEDIA_TYPES,
69 } );
70 }
71 }
72 }
73
74 toggleAttribute( attribute ) {
75 return ( newValue ) => {
76 this.props.setAttributes( { [ attribute ]: newValue } );
77 };
78 }
79
80 onSelectURL( newSrc ) {
81 const { attributes, setAttributes } = this.props;
82 const { src } = attributes;
83
84 // Set the block's src from the edit component's state, and switch off
85 // the editing UI.
86 if ( newSrc !== src ) {
87 // Check if there's an embed block that handles this URL.
88 const embedBlock = createUpgradedEmbedBlock(
89 { attributes: { url: newSrc } }
90 );
91 if ( undefined !== embedBlock ) {
92 this.props.onReplace( embedBlock );
93 return;
94 }
95 setAttributes( { src: newSrc, id: undefined } );
96 }
97
98 this.setState( { editing: false } );
99 }
100
101 render() {
102 const { autoplay, caption, loop, preload, src } = this.props.attributes;
103 const { setAttributes, isSelected, className, noticeOperations, noticeUI } = this.props;
104 const { editing } = this.state;
105 const switchToEditing = () => {
106 this.setState( { editing: true } );
107 };
108 const onSelectAudio = ( media ) => {
109 if ( ! media || ! media.url ) {
110 // in this case there was an error and we should continue in the editing state
111 // previous attributes should be removed because they may be temporary blob urls
112 setAttributes( { src: undefined, id: undefined } );
113 switchToEditing();
114 return;
115 }
116 // sets the block's attribute and updates the edit component from the
117 // selected media, then switches off the editing UI
118 setAttributes( { src: media.url, id: media.id } );
119 this.setState( { src: media.url, editing: false } );
120 };
121 if ( editing ) {
122 return (
123 <MediaPlaceholder
124 icon={ <BlockIcon icon={ icon } /> }
125 className={ className }
126 onSelect={ onSelectAudio }
127 onSelectURL={ this.onSelectURL }
128 accept="audio/*"
129 allowedTypes={ ALLOWED_MEDIA_TYPES }
130 value={ this.props.attributes }
131 notices={ noticeUI }
132 onError={ noticeOperations.createErrorNotice }
133 />
134 );
135 }
136
137 /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
138 return (
139 <Fragment>
140 <BlockControls>
141 <Toolbar>
142 <IconButton
143 className="components-icon-button components-toolbar__control"
144 label={ __( 'Edit audio' ) }
145 onClick={ switchToEditing }
146 icon="edit"
147 />
148 </Toolbar>
149 </BlockControls>
150 <InspectorControls>
151 <PanelBody title={ __( 'Audio Settings' ) }>
152 <ToggleControl
153 label={ __( 'Autoplay' ) }
154 onChange={ this.toggleAttribute( 'autoplay' ) }
155 checked={ autoplay }
156 />
157 <ToggleControl
158 label={ __( 'Loop' ) }
159 onChange={ this.toggleAttribute( 'loop' ) }
160 checked={ loop }
161 />
162 <SelectControl
163 label={ __( 'Preload' ) }
164 value={ undefined !== preload ? preload : 'none' }
165 // `undefined` is required for the preload attribute to be unset.
166 onChange={ ( value ) => setAttributes( { preload: ( 'none' !== value ) ? value : undefined } ) }
167 options={ [
168 { value: 'auto', label: __( 'Auto' ) },
169 { value: 'metadata', label: __( 'Metadata' ) },
170 { value: 'none', label: __( 'None' ) },
171 ] }
172 />
173 </PanelBody>
174 </InspectorControls>
175 <figure className={ className }>
176 { /*
177 Disable the audio tag so the user clicking on it won't play the
178 file or change the position slider when the controls are enabled.
179 */ }
180 <Disabled>
181 <audio controls="controls" src={ src } />
182 </Disabled>
183 { ( ! RichText.isEmpty( caption ) || isSelected ) && (
184 <RichText
185 tagName="figcaption"
186 placeholder={ __( 'Write caption…' ) }
187 value={ caption }
188 onChange={ ( value ) => setAttributes( { caption: value } ) }
189 inlineToolbar
190 />
191 ) }
192 </figure>
193 </Fragment>
194 );
195 /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
196 }
197}
198
199export default withNotices( AudioEdit );