UNPKG

@wordpress/block-library

Version:
266 lines (254 loc) 8.92 kB
/* 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