UNPKG

@wordpress/block-library

Version:
358 lines (319 loc) 11.3 kB
import { createElement } from "@wordpress/element"; /** * External dependencies */ import classnames from 'classnames'; import { get, isEmpty, pick } from 'lodash'; /** * WordPress dependencies */ import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob'; import { Placeholder } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; import { BlockAlignmentControl, BlockControls, BlockIcon, MediaPlaceholder, useBlockProps, store as blockEditorStore, __experimentalUseBorderProps as useBorderProps } from '@wordpress/block-editor'; import { useEffect, useRef, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { image as icon } from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies */ import Image from './image'; // Much of this description is duplicated from MediaPlaceholder. const placeholder = content => { return createElement(Placeholder, { className: "block-editor-media-placeholder", withIllustration: true, icon: icon, label: __('Image'), instructions: __('Upload an image file, pick one from your media library, or add one with a URL.') }, content); }; /** * Module constants */ import { LINK_DESTINATION_ATTACHMENT, LINK_DESTINATION_CUSTOM, LINK_DESTINATION_MEDIA, LINK_DESTINATION_NONE, ALLOWED_MEDIA_TYPES } from './constants'; export const pickRelevantMediaFiles = (image, size) => { const imageProps = pick(image, ['alt', 'id', 'link', 'caption']); imageProps.url = get(image, ['sizes', size, 'url']) || get(image, ['media_details', 'sizes', size, 'source_url']) || image.url; return imageProps; }; /** * Is the URL a temporary blob URL? A blob URL is one that is used temporarily * while the image is being uploaded and will not have an id yet allocated. * * @param {number=} id The id of the image. * @param {string=} url The url of the image. * * @return {boolean} Is the URL a Blob URL */ const isTemporaryImage = (id, url) => !id && isBlobURL(url); /** * Is the url for the image hosted externally. An externally hosted image has no * id and is not a blob url. * * @param {number=} id The id of the image. * @param {string=} url The url of the image. * * @return {boolean} Is the url an externally hosted url? */ export const isExternalImage = (id, url) => url && !id && !isBlobURL(url); /** * Checks if WP generated default image size. Size generation is skipped * when the image is smaller than the said size. * * @param {Object} image * @param {string} defaultSize * * @return {boolean} Whether or not it has default image size. */ function hasDefaultSize(image, defaultSize) { var _image$sizes$defaultS, _image$sizes, _image$media_details$, _image$media_details, _image$media_details$2; return 'url' in ((_image$sizes$defaultS = image === null || image === void 0 ? void 0 : (_image$sizes = image.sizes) === null || _image$sizes === void 0 ? void 0 : _image$sizes[defaultSize]) !== null && _image$sizes$defaultS !== void 0 ? _image$sizes$defaultS : {}) || 'source_url' in ((_image$media_details$ = image === null || image === void 0 ? void 0 : (_image$media_details = image.media_details) === null || _image$media_details === void 0 ? void 0 : (_image$media_details$2 = _image$media_details.sizes) === null || _image$media_details$2 === void 0 ? void 0 : _image$media_details$2[defaultSize]) !== null && _image$media_details$ !== void 0 ? _image$media_details$ : {}); } export function ImageEdit(_ref) { let { attributes, setAttributes, isSelected, className, insertBlocksAfter, onReplace, context, clientId } = _ref; const { url = '', alt, caption, align, id, width, height, sizeSlug } = attributes; const [temporaryURL, setTemporaryURL] = useState(); const altRef = useRef(); useEffect(() => { altRef.current = alt; }, [alt]); const captionRef = useRef(); useEffect(() => { captionRef.current = caption; }, [caption]); const ref = useRef(); const { imageDefaultSize, mediaUpload, isContentLocked } = useSelect(select => { const { getSettings, __unstableGetContentLockingParent } = select(blockEditorStore); const settings = getSettings(); return { imageDefaultSize: settings.imageDefaultSize, mediaUpload: settings.mediaUpload, isContentLocked: !!__unstableGetContentLockingParent(clientId) }; }, []); const { createErrorNotice } = useDispatch(noticesStore); function onUploadError(message) { createErrorNotice(message, { type: 'snackbar' }); setAttributes({ src: undefined, id: undefined, url: undefined }); setTemporaryURL(undefined); } function onSelectImage(media) { var _window, _window$wp, _window$wp$media, _window$wp$media$view, _window$wp$media$view2, _window$wp$media$view3; if (!media || !media.url) { setAttributes({ url: undefined, alt: undefined, id: undefined, title: undefined, caption: undefined }); return; } if (isBlobURL(media.url)) { setTemporaryURL(media.url); return; } setTemporaryURL(); let mediaAttributes = pickRelevantMediaFiles(media, imageDefaultSize); // If a caption text was meanwhile written by the user, // make sure the text is not overwritten by empty captions. if (captionRef.current && !get(mediaAttributes, ['caption'])) { const { caption: omittedCaption, ...restMediaAttributes } = mediaAttributes; mediaAttributes = restMediaAttributes; } let additionalAttributes; // Reset the dimension attributes if changing to a different image. if (!media.id || media.id !== id) { additionalAttributes = { width: undefined, height: undefined, // Fallback to size "full" if there's no default image size. // It means the image is smaller, and the block will use a full-size URL. sizeSlug: hasDefaultSize(media, imageDefaultSize) ? imageDefaultSize : 'full' }; } else { // Keep the same url when selecting the same file, so "Image Size" // option is not changed. additionalAttributes = { url }; } // Check if default link setting should be used. let linkDestination = attributes.linkDestination; if (!linkDestination) { // Use the WordPress option to determine the proper default. // The constants used in Gutenberg do not match WP options so a little more complicated than ideal. // TODO: fix this in a follow up PR, requires updating media-text and ui component. switch (((_window = window) === null || _window === void 0 ? void 0 : (_window$wp = _window.wp) === null || _window$wp === void 0 ? void 0 : (_window$wp$media = _window$wp.media) === null || _window$wp$media === void 0 ? void 0 : (_window$wp$media$view = _window$wp$media.view) === null || _window$wp$media$view === void 0 ? void 0 : (_window$wp$media$view2 = _window$wp$media$view.settings) === null || _window$wp$media$view2 === void 0 ? void 0 : (_window$wp$media$view3 = _window$wp$media$view2.defaultProps) === null || _window$wp$media$view3 === void 0 ? void 0 : _window$wp$media$view3.link) || LINK_DESTINATION_NONE) { case 'file': case LINK_DESTINATION_MEDIA: linkDestination = LINK_DESTINATION_MEDIA; break; case 'post': case LINK_DESTINATION_ATTACHMENT: linkDestination = LINK_DESTINATION_ATTACHMENT; break; case LINK_DESTINATION_CUSTOM: linkDestination = LINK_DESTINATION_CUSTOM; break; case LINK_DESTINATION_NONE: linkDestination = LINK_DESTINATION_NONE; break; } } // Check if the image is linked to it's media. let href; switch (linkDestination) { case LINK_DESTINATION_MEDIA: href = media.url; break; case LINK_DESTINATION_ATTACHMENT: href = media.link; break; } mediaAttributes.href = href; setAttributes({ ...mediaAttributes, ...additionalAttributes, linkDestination }); } function onSelectURL(newURL) { if (newURL !== url) { setAttributes({ url: newURL, id: undefined, width: undefined, height: undefined, sizeSlug: imageDefaultSize }); } } function updateAlignment(nextAlign) { const extraUpdatedAttributes = ['wide', 'full'].includes(nextAlign) ? { width: undefined, height: undefined } : {}; setAttributes({ ...extraUpdatedAttributes, align: nextAlign }); } let isTemp = isTemporaryImage(id, url); // Upload a temporary image on mount. useEffect(() => { if (!isTemp) { return; } const file = getBlobByURL(url); if (file) { mediaUpload({ filesList: [file], onFileChange: _ref2 => { let [img] = _ref2; onSelectImage(img); }, allowedTypes: ALLOWED_MEDIA_TYPES, onError: message => { isTemp = false; onUploadError(message); } }); } }, []); // If an image is temporary, revoke the Blob url when it is uploaded (and is // no longer temporary). useEffect(() => { if (isTemp) { setTemporaryURL(url); return; } revokeBlobURL(temporaryURL); }, [isTemp, url]); const isExternal = isExternalImage(id, url); const src = isExternal ? url : undefined; const mediaPreview = !!url && createElement("img", { alt: __('Edit image'), title: __('Edit image'), className: 'edit-image-preview', src: url }); const borderProps = useBorderProps(attributes); const classes = classnames(className, { 'is-transient': temporaryURL, 'is-resized': !!width || !!height, [`size-${sizeSlug}`]: sizeSlug, 'has-custom-border': !!borderProps.className || !isEmpty(borderProps.style) }); const blockProps = useBlockProps({ ref, className: classes }); return createElement("figure", blockProps, (temporaryURL || url) && createElement(Image, { temporaryURL: temporaryURL, attributes: attributes, setAttributes: setAttributes, isSelected: isSelected, insertBlocksAfter: insertBlocksAfter, onReplace: onReplace, onSelectImage: onSelectImage, onSelectURL: onSelectURL, onUploadError: onUploadError, containerRef: ref, context: context, clientId: clientId, isContentLocked: isContentLocked }), !url && !isContentLocked && createElement(BlockControls, { group: "block" }, createElement(BlockAlignmentControl, { value: align, onChange: updateAlignment })), createElement(MediaPlaceholder, { icon: createElement(BlockIcon, { icon: icon }), onSelect: onSelectImage, onSelectURL: onSelectURL, onError: onUploadError, placeholder: placeholder, accept: "image/*", allowedTypes: ALLOWED_MEDIA_TYPES, value: { id, src }, mediaPreview: mediaPreview, disableMediaButtons: temporaryURL || url })); } export default ImageEdit; //# sourceMappingURL=edit.js.map