@wordpress/block-library
Version:
Block library for the WordPress editor.
266 lines (254 loc) • 8.92 kB
JavaScript
/* wp:polyfill */
/**
* Internal dependencies
*/
import { createUpgradedEmbedBlock, getClassNames, removeAspectRatioClasses, fallback, getEmbedInfoByProvider, getMergedAttributesWithPreview } from './util';
import EmbedControls from './embed-controls';
import { embedContentIcon } from './icons';
import EmbedLoading from './embed-loading';
import EmbedPlaceholder from './embed-placeholder';
import EmbedPreview from './embed-preview';
/**
* External dependencies
*/
import clsx from 'clsx';
/**
* WordPress dependencies
*/
import { __, _x, sprintf } from '@wordpress/i18n';
import { useState, useEffect } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { useBlockProps } from '@wordpress/block-editor';
import { store as coreStore } from '@wordpress/core-data';
import { View } from '@wordpress/primitives';
import { getAuthority } from '@wordpress/url';
import { Caption } from '../utils/caption';
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
const EmbedEdit = props => {
const {
attributes: {
providerNameSlug,
previewable,
responsive,
url: attributesUrl
},
attributes,
isSelected,
onReplace,
setAttributes,
insertBlocksAfter,
onFocus
} = props;
const defaultEmbedInfo = {
title: _x('Embed', 'block title'),
icon: embedContentIcon
};
const {
icon,
title
} = getEmbedInfoByProvider(providerNameSlug) || defaultEmbedInfo;
const [url, setURL] = useState(attributesUrl);
const [isEditingURL, setIsEditingURL] = useState(false);
const {
invalidateResolution
} = useDispatch(coreStore);
const {
preview,
fetching,
themeSupportsResponsive,
cannotEmbed,
hasResolved
} = useSelect(select => {
const {
getEmbedPreview,
isPreviewEmbedFallback,
isRequestingEmbedPreview,
getThemeSupports,
hasFinishedResolution
} = select(coreStore);
if (!attributesUrl) {
return {
fetching: false,
cannotEmbed: false
};
}
const embedPreview = getEmbedPreview(attributesUrl);
const previewIsFallback = isPreviewEmbedFallback(attributesUrl);
// The external oEmbed provider does not exist. We got no type info and no html.
const badEmbedProvider = embedPreview?.html === false && embedPreview?.type === undefined;
// Some WordPress URLs that can't be embedded will cause the API to return
// a valid JSON response with no HTML and `data.status` set to 404, rather
// than generating a fallback response as other embeds do.
const wordpressCantEmbed = embedPreview?.data?.status === 404;
const validPreview = !!embedPreview && !badEmbedProvider && !wordpressCantEmbed;
return {
preview: validPreview ? embedPreview : undefined,
fetching: isRequestingEmbedPreview(attributesUrl),
themeSupportsResponsive: getThemeSupports()['responsive-embeds'],
cannotEmbed: !validPreview || previewIsFallback,
hasResolved: hasFinishedResolution('getEmbedPreview', [attributesUrl])
};
}, [attributesUrl]);
/**
* Returns the attributes derived from the preview, merged with the current attributes.
*
* @return {Object} Merged attributes.
*/
const getMergedAttributes = () => getMergedAttributesWithPreview(attributes, preview, title, responsive);
function toggleResponsive(newAllowResponsive) {
const {
className
} = attributes;
const {
html
} = preview;
setAttributes({
allowResponsive: newAllowResponsive,
className: getClassNames(html, className, responsive && newAllowResponsive)
});
}
useEffect(() => {
if (preview?.html || !cannotEmbed || !hasResolved) {
return;
}
// At this stage, we're not fetching the preview and know it can't be embedded,
// so try removing any trailing slash, and resubmit.
const newURL = attributesUrl.replace(/\/$/, '');
setURL(newURL);
setIsEditingURL(false);
setAttributes({
url: newURL
});
}, [preview?.html, attributesUrl, cannotEmbed, hasResolved, setAttributes]);
// Try a different provider in case the embed url is not supported.
useEffect(() => {
if (!cannotEmbed || fetching || !url) {
return;
}
// Until X provider is supported in WordPress, as a workaround we use Twitter provider.
if (getAuthority(url) === 'x.com') {
const newURL = new URL(url);
newURL.host = 'twitter.com';
setAttributes({
url: newURL.toString()
});
}
}, [url, cannotEmbed, fetching, setAttributes]);
// Handle incoming preview.
useEffect(() => {
if (preview && !isEditingURL) {
// When obtaining an incoming preview,
// we set the attributes derived from the preview data.
const mergedAttributes = getMergedAttributes();
const hasChanges = Object.keys(mergedAttributes).some(key => mergedAttributes[key] !== attributes[key]);
if (hasChanges) {
setAttributes(mergedAttributes);
}
if (onReplace) {
const upgradedBlock = createUpgradedEmbedBlock(props, mergedAttributes);
if (upgradedBlock) {
onReplace(upgradedBlock);
}
}
}
}, [preview, isEditingURL]);
const blockProps = useBlockProps();
if (fetching) {
return /*#__PURE__*/_jsx(View, {
...blockProps,
children: /*#__PURE__*/_jsx(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.
const showEmbedPlaceholder = !preview || cannotEmbed || isEditingURL;
if (showEmbedPlaceholder) {
return /*#__PURE__*/_jsx(View, {
...blockProps,
children: /*#__PURE__*/_jsx(EmbedPlaceholder, {
icon: icon,
label: label,
onFocus: onFocus,
onSubmit: event => {
if (event) {
event.preventDefault();
}
// If the embed URL was changed, we need to reset the aspect ratio class.
// To do this we have to remove the existing ratio class so it can be recalculated.
const blockClass = removeAspectRatioClasses(attributes.className);
setIsEditingURL(false);
setAttributes({
url,
className: blockClass
});
},
value: url,
cannotEmbed: cannotEmbed,
onChange: value => setURL(value),
fallback: () => fallback(url, onReplace),
tryAgain: () => {
invalidateResolution('getEmbedPreview', [url]);
}
})
});
}
// Even though we set attributes that get derived from the preview,
// we don't access them directly because for the initial render,
// the `setAttributes` call will not have taken effect. If we're
// rendering responsive content, setting the responsive classes
// after the preview has been rendered can result in unwanted
// clipping or scrollbars. The `getAttributesFromPreview` function
// that `getMergedAttributes` uses is memoized so that we're not
// calculating them on every render.
const {
caption,
type,
allowResponsive,
className: classFromPreview
} = getMergedAttributes();
const className = clsx(classFromPreview, props.className);
return /*#__PURE__*/_jsxs(_Fragment, {
children: [/*#__PURE__*/_jsx(EmbedControls, {
showEditButton: preview && !cannotEmbed,
themeSupportsResponsive: themeSupportsResponsive,
blockSupportsResponsive: responsive,
allowResponsive: allowResponsive,
toggleResponsive: toggleResponsive,
switchBackToURLInput: () => setIsEditingURL(true)
}), /*#__PURE__*/_jsxs("figure", {
...blockProps,
className: clsx(blockProps.className, className, {
[`is-type-${type}`]: type,
[`is-provider-${providerNameSlug}`]: providerNameSlug,
[`wp-block-embed-${providerNameSlug}`]: providerNameSlug
}),
children: [/*#__PURE__*/_jsx(EmbedPreview, {
preview: preview,
previewable: previewable,
className: className,
url: url,
type: type,
caption: caption,
onCaptionChange: value => setAttributes({
caption: value
}),
isSelected: isSelected,
icon: icon,
label: label,
insertBlocksAfter: insertBlocksAfter,
attributes: attributes,
setAttributes: setAttributes
}), /*#__PURE__*/_jsx(Caption, {
attributes: attributes,
setAttributes: setAttributes,
isSelected: isSelected,
insertBlocksAfter: insertBlocksAfter,
label: __('Embed caption text'),
showToolbarButton: isSelected
})]
})]
});
};
export default EmbedEdit;
//# sourceMappingURL=edit.js.map