@wordpress/block-library
Version:
Block library for the WordPress editor.
209 lines (183 loc) • 6.66 kB
JavaScript
/**
* Internal dependencies
*/
import { isFromWordPress, createUpgradedEmbedBlock, getClassNames, fallback } from './util';
import EmbedControls from './embed-controls';
import EmbedLoading from './embed-loading';
import EmbedPlaceholder from './embed-placeholder';
import EmbedPreview from './embed-preview';
/**
* External dependencies
*/
import { kebabCase, toLower } from 'lodash';
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
export function getEmbedEditComponent( title, icon, responsive = true ) {
return class extends Component {
constructor() {
super( ...arguments );
this.switchBackToURLInput = this.switchBackToURLInput.bind( this );
this.setUrl = this.setUrl.bind( this );
this.getAttributesFromPreview = this.getAttributesFromPreview.bind( this );
this.setAttributesFromPreview = this.setAttributesFromPreview.bind( this );
this.getResponsiveHelp = this.getResponsiveHelp.bind( this );
this.toggleResponsive = this.toggleResponsive.bind( this );
this.handleIncomingPreview = this.handleIncomingPreview.bind( this );
this.state = {
editingURL: false,
url: this.props.attributes.url,
};
if ( this.props.preview ) {
this.handleIncomingPreview();
}
}
handleIncomingPreview() {
const { allowResponsive } = this.props.attributes;
this.setAttributesFromPreview();
const upgradedBlock = createUpgradedEmbedBlock(
this.props,
this.getAttributesFromPreview( this.props.preview, allowResponsive )
);
if ( upgradedBlock ) {
this.props.onReplace( upgradedBlock );
}
}
componentDidUpdate( prevProps ) {
const hasPreview = undefined !== this.props.preview;
const hadPreview = undefined !== prevProps.preview;
const previewChanged = prevProps.preview && this.props.preview && this.props.preview.html !== prevProps.preview.html;
const switchedPreview = previewChanged || ( hasPreview && ! hadPreview );
const switchedURL = this.props.attributes.url !== prevProps.attributes.url;
if ( switchedPreview || switchedURL ) {
if ( this.props.cannotEmbed ) {
// We either have a new preview or a new URL, but we can't embed it.
if ( ! this.props.fetching ) {
// If we're not fetching the preview, then we know it can't be embedded, so try
// removing any trailing slash, and resubmit.
this.resubmitWithoutTrailingSlash();
}
return;
}
this.handleIncomingPreview();
}
}
resubmitWithoutTrailingSlash() {
this.setState( ( prevState ) => ( {
url: prevState.url.replace( /\/$/, '' ),
} ), this.setUrl );
}
setUrl( event ) {
if ( event ) {
event.preventDefault();
}
const { url } = this.state;
const { setAttributes } = this.props;
this.setState( { editingURL: false } );
setAttributes( { url } );
}
/***
* Gets block attributes based on the preview and responsive state.
*
* @param {string} preview The preview data.
* @param {boolean} allowResponsive Apply responsive classes to fixed size content.
* @return {Object} Attributes and values.
*/
getAttributesFromPreview( preview, allowResponsive = true ) {
const attributes = {};
// Some plugins only return HTML with no type info, so default this to 'rich'.
let { type = 'rich' } = preview;
// If we got a provider name from the API, use it for the slug, otherwise we use the title,
// because not all embed code gives us a provider name.
const { html, provider_name: providerName } = preview;
const providerNameSlug = kebabCase( toLower( '' !== providerName ? providerName : title ) );
if ( isFromWordPress( html ) ) {
type = 'wp-embed';
}
if ( html || 'photo' === type ) {
attributes.type = type;
attributes.providerNameSlug = providerNameSlug;
}
attributes.className = getClassNames( html, this.props.attributes.className, responsive && allowResponsive );
return attributes;
}
/***
* Sets block attributes based on the preview data.
*/
setAttributesFromPreview() {
const { setAttributes, preview } = this.props;
const { allowResponsive } = this.props.attributes;
setAttributes( this.getAttributesFromPreview( preview, allowResponsive ) );
}
switchBackToURLInput() {
this.setState( { editingURL: true } );
}
getResponsiveHelp( checked ) {
return checked ? __( 'This embed will preserve its aspect ratio when the browser is resized.' ) : __( 'This embed may not preserve its aspect ratio when the browser is resized.' );
}
toggleResponsive() {
const { allowResponsive, className } = this.props.attributes;
const { html } = this.props.preview;
const newAllowResponsive = ! allowResponsive;
this.props.setAttributes(
{
allowResponsive: newAllowResponsive,
className: getClassNames( html, className, responsive && newAllowResponsive ),
}
);
}
render() {
const { url, editingURL } = this.state;
const { caption, type, allowResponsive } = this.props.attributes;
const { fetching, setAttributes, isSelected, className, preview, cannotEmbed, themeSupportsResponsive, tryAgain } = this.props;
if ( fetching ) {
return (
<EmbedLoading />
);
}
// translators: %s: type of embed e.g: "YouTube", "Twitter", etc. "Embed" is used when no specific type exists
const label = sprintf( __( '%s URL' ), title );
// No preview, or we can't embed the current URL, or we've clicked the edit button.
if ( ! preview || cannotEmbed || editingURL ) {
return (
<EmbedPlaceholder
icon={ icon }
label={ label }
onSubmit={ this.setUrl }
value={ url }
cannotEmbed={ cannotEmbed }
onChange={ ( event ) => this.setState( { url: event.target.value } ) }
fallback={ () => fallback( url, this.props.onReplace ) }
tryAgain={ tryAgain }
/>
);
}
return (
<Fragment>
<EmbedControls
showEditButton={ preview && ! cannotEmbed }
themeSupportsResponsive={ themeSupportsResponsive }
blockSupportsResponsive={ responsive }
allowResponsive={ allowResponsive }
getResponsiveHelp={ this.getResponsiveHelp }
toggleResponsive={ this.toggleResponsive }
switchBackToURLInput={ this.switchBackToURLInput }
/>
<EmbedPreview
preview={ preview }
className={ className }
url={ url }
type={ type }
caption={ caption }
onCaptionChange={ ( value ) => setAttributes( { caption: value } ) }
isSelected={ isSelected }
icon={ icon }
label={ label }
/>
</Fragment>
);
}
};
}