UNPKG

@wordpress/block-library

Version:
430 lines (390 loc) 12.7 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import { createElement, Fragment } from "@wordpress/element"; /** * External dependencies */ import { every, filter, find, get, isEmpty, map, reduce, some } from 'lodash'; /** * WordPress dependencies */ import { compose } from '@wordpress/compose'; import { PanelBody, SelectControl, ToggleControl, withNotices, RangeControl } from '@wordpress/components'; import { MediaPlaceholder, InspectorControls, useBlockProps, store as blockEditorStore } from '@wordpress/block-editor'; import { Platform, useEffect, useState, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob'; import { useDispatch, useSelect } from '@wordpress/data'; import { withViewportMatch } from '@wordpress/viewport'; import { View } from '@wordpress/primitives'; import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ import { sharedIcon } from '../shared-icon'; import { pickRelevantMediaFiles } from './shared'; import { defaultColumnsNumberV1 } from '../deprecated'; import Gallery from './gallery'; import { LINK_DESTINATION_ATTACHMENT, LINK_DESTINATION_MEDIA, LINK_DESTINATION_NONE } from './constants'; const MAX_COLUMNS = 8; const linkOptions = [{ value: LINK_DESTINATION_ATTACHMENT, label: __('Attachment Page') }, { value: LINK_DESTINATION_MEDIA, label: __('Media File') }, { value: LINK_DESTINATION_NONE, label: __('None') }]; const ALLOWED_MEDIA_TYPES = ['image']; const PLACEHOLDER_TEXT = Platform.select({ web: __('Drag images, upload new ones or select files from your library.'), native: __('ADD MEDIA') }); const MOBILE_CONTROL_PROPS_RANGE_CONTROL = Platform.select({ web: {}, native: { type: 'stepper' } }); function GalleryEdit(props) { const { attributes, clientId, isSelected, noticeUI, noticeOperations, onFocus } = props; const { columns = defaultColumnsNumberV1(attributes), imageCrop, images, linkTo, sizeSlug } = attributes; const [selectedImage, setSelectedImage] = useState(); const [attachmentCaptions, setAttachmentCaptions] = useState(); const { __unstableMarkNextChangeAsNotPersistent } = useDispatch(blockEditorStore); const { imageSizes, mediaUpload, getMedia, wasBlockJustInserted } = useSelect(select => { const settings = select(blockEditorStore).getSettings(); return { imageSizes: settings.imageSizes, mediaUpload: settings.mediaUpload, getMedia: select(coreStore).getMedia, wasBlockJustInserted: select(blockEditorStore).wasBlockJustInserted(clientId, 'inserter_menu') }; }); const resizedImages = useMemo(() => { if (isSelected) { return reduce(attributes.ids, (currentResizedImages, id) => { if (!id) { return currentResizedImages; } const image = getMedia(id); const sizes = reduce(imageSizes, (currentSizes, size) => { const defaultUrl = get(image, ['sizes', size.slug, 'url']); const mediaDetailsUrl = get(image, ['media_details', 'sizes', size.slug, 'source_url']); return { ...currentSizes, [size.slug]: defaultUrl || mediaDetailsUrl }; }, {}); return { ...currentResizedImages, [parseInt(id, 10)]: sizes }; }, {}); } return {}; }, [isSelected, attributes.ids, imageSizes]); function onFocusGalleryCaption() { setSelectedImage(); } function setAttributes(newAttrs) { if (newAttrs.ids) { throw new Error('The "ids" attribute should not be changed directly. It is managed automatically when "images" attribute changes'); } if (newAttrs.images) { newAttrs = { ...newAttrs, // Unlike images[ n ].id which is a string, always ensure the // ids array contains numbers as per its attribute type. ids: map(newAttrs.images, _ref => { let { id } = _ref; return parseInt(id, 10); }) }; } props.setAttributes(newAttrs); } function onSelectImage(index) { return () => { setSelectedImage(index); }; } function onDeselectImage() { return () => { setSelectedImage(); }; } function onMove(oldIndex, newIndex) { const newImages = [...images]; newImages.splice(newIndex, 1, images[oldIndex]); newImages.splice(oldIndex, 1, images[newIndex]); setSelectedImage(newIndex); setAttributes({ images: newImages }); } function onMoveForward(oldIndex) { return () => { if (oldIndex === images.length - 1) { return; } onMove(oldIndex, oldIndex + 1); }; } function onMoveBackward(oldIndex) { return () => { if (oldIndex === 0) { return; } onMove(oldIndex, oldIndex - 1); }; } function onRemoveImage(index) { return () => { const newImages = filter(images, (img, i) => index !== i); setSelectedImage(); setAttributes({ images: newImages, columns: attributes.columns ? Math.min(newImages.length, attributes.columns) : attributes.columns }); }; } function selectCaption(newImage) { // The image id in both the images and attachmentCaptions arrays is a // string, so ensure comparison works correctly by converting the // newImage.id to a string. const newImageId = newImage.id.toString(); const currentImage = find(images, { id: newImageId }); const currentImageCaption = currentImage ? currentImage.caption : newImage.caption; if (!attachmentCaptions) { return currentImageCaption; } const attachment = find(attachmentCaptions, { id: newImageId }); // If the attachment caption is updated. if (attachment && attachment.caption !== newImage.caption) { return newImage.caption; } return currentImageCaption; } function onSelectImages(newImages) { setAttachmentCaptions(newImages.map(newImage => ({ // Store the attachmentCaption id as a string for consistency // with the type of the id in the images attribute. id: newImage.id.toString(), caption: newImage.caption }))); setAttributes({ images: newImages.map(newImage => ({ ...pickRelevantMediaFiles(newImage, sizeSlug), caption: selectCaption(newImage, images, attachmentCaptions), // The id value is stored in a data attribute, so when the // block is parsed it's converted to a string. Converting // to a string here ensures it's type is consistent. id: newImage.id.toString() })), columns: attributes.columns ? Math.min(newImages.length, attributes.columns) : attributes.columns }); } function onUploadError(message) { noticeOperations.removeAllNotices(); noticeOperations.createErrorNotice(message); } function setLinkTo(value) { setAttributes({ linkTo: value }); } function setColumnsNumber(value) { setAttributes({ columns: value }); } function toggleImageCrop() { setAttributes({ imageCrop: !imageCrop }); } function getImageCropHelp(checked) { return checked ? __('Thumbnails are cropped to align.') : __('Thumbnails are not cropped.'); } function setImageAttributes(index, newAttributes) { if (!images[index]) { return; } setAttributes({ images: [...images.slice(0, index), { ...images[index], ...newAttributes }, ...images.slice(index + 1)] }); } function getImagesSizeOptions() { return map(filter(imageSizes, _ref2 => { let { slug } = _ref2; return some(resizedImages, sizes => sizes[slug]); }), _ref3 => { let { name, slug } = _ref3; return { value: slug, label: name }; }); } function updateImagesSize(newSizeSlug) { const updatedImages = map(images, image => { if (!image.id) { return image; } const url = get(resizedImages, [parseInt(image.id, 10), newSizeSlug]); return { ...image, ...(url && { url }) }; }); setAttributes({ images: updatedImages, sizeSlug: newSizeSlug }); } useEffect(() => { if (Platform.OS === 'web' && images && images.length > 0 && every(images, _ref4 => { let { url } = _ref4; return isBlobURL(url); })) { const filesList = map(images, _ref5 => { let { url } = _ref5; return getBlobByURL(url); }); images.forEach(_ref6 => { let { url } = _ref6; return revokeBlobURL(url); }); mediaUpload({ filesList, onFileChange: onSelectImages, allowedTypes: ['image'] }); } }, []); useEffect(() => { // Deselect images when deselecting the block. if (!isSelected) { setSelectedImage(); } }, [isSelected]); useEffect(() => { // linkTo attribute must be saved so blocks don't break when changing // image_default_link_type in options.php. if (!linkTo) { var _window, _window$wp, _window$wp$media, _window$wp$media$view, _window$wp$media$view2, _window$wp$media$view3; __unstableMarkNextChangeAsNotPersistent(); setAttributes({ linkTo: ((_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 }); } }, [linkTo]); const hasImages = !!images.length; const hasImageIds = hasImages && images.some(image => !!image.id); const mediaPlaceholder = createElement(MediaPlaceholder, { addToGallery: hasImageIds, isAppender: hasImages, disableMediaButtons: hasImages && !isSelected, icon: !hasImages && sharedIcon, labels: { title: !hasImages && __('Gallery'), instructions: !hasImages && PLACEHOLDER_TEXT }, onSelect: onSelectImages, accept: "image/*", allowedTypes: ALLOWED_MEDIA_TYPES, multiple: true, value: hasImageIds ? images : {}, onError: onUploadError, notices: hasImages ? undefined : noticeUI, onFocus: onFocus, autoOpenMediaUpload: !hasImages && isSelected && wasBlockJustInserted }); const blockProps = useBlockProps(); if (!hasImages) { return createElement(View, blockProps, mediaPlaceholder); } const imageSizeOptions = getImagesSizeOptions(); const shouldShowSizeOptions = hasImages && !isEmpty(imageSizeOptions); return createElement(Fragment, null, createElement(InspectorControls, null, createElement(PanelBody, { title: __('Settings') }, images.length > 1 && createElement(RangeControl, _extends({ label: __('Columns'), value: columns, onChange: setColumnsNumber, min: 1, max: Math.min(MAX_COLUMNS, images.length) }, MOBILE_CONTROL_PROPS_RANGE_CONTROL, { required: true })), createElement(ToggleControl, { label: __('Crop images'), checked: !!imageCrop, onChange: toggleImageCrop, help: getImageCropHelp }), createElement(SelectControl, { label: __('Link to'), value: linkTo, onChange: setLinkTo, options: linkOptions, hideCancelButton: true }), shouldShowSizeOptions && createElement(SelectControl, { label: __('Image size'), value: sizeSlug, options: imageSizeOptions, onChange: updateImagesSize, hideCancelButton: true }))), noticeUI, createElement(Gallery, _extends({}, props, { selectedImage: selectedImage, mediaPlaceholder: mediaPlaceholder, onMoveBackward: onMoveBackward, onMoveForward: onMoveForward, onRemoveImage: onRemoveImage, onSelectImage: onSelectImage, onDeselectImage: onDeselectImage, onSetImageAttributes: setImageAttributes, blockProps: blockProps // This prop is used by gallery.native.js. , onFocusGalleryCaption: onFocusGalleryCaption }))); } export default compose([withNotices, withViewportMatch({ isNarrow: '< small' })])(GalleryEdit); //# sourceMappingURL=edit.js.map