UNPKG

@wordpress/block-library

Version:
358 lines (322 loc) • 11.3 kB
import { createElement, Fragment } from "@wordpress/element"; /** * External dependencies */ import classnames from 'classnames'; import { map, filter } from 'lodash'; /** * WordPress dependencies */ import { __, _x } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { useState, useRef } from '@wordpress/element'; import { BlockControls, BlockVerticalAlignmentControl, useInnerBlocksProps, InspectorControls, useBlockProps, __experimentalImageURLInputUI as ImageURLInputUI, __experimentalImageSizeControl as ImageSizeControl, store as blockEditorStore } from '@wordpress/block-editor'; import { PanelBody, RangeControl, TextareaControl, ToggleControl, ToolbarButton, ExternalLink, FocalPointPicker } from '@wordpress/components'; import { isBlobURL, getBlobTypeByURL } from '@wordpress/blob'; import { pullLeft, pullRight } from '@wordpress/icons'; import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ import MediaContainer from './media-container'; import { DEFAULT_MEDIA_SIZE_SLUG } from './constants'; /** * Constants */ const TEMPLATE = [['core/paragraph', { placeholder: _x('Content…', 'content placeholder') }]]; // this limits the resize to a safe zone to avoid making broken layouts const WIDTH_CONSTRAINT_PERCENTAGE = 15; const applyWidthConstraints = width => Math.max(WIDTH_CONSTRAINT_PERCENTAGE, Math.min(width, 100 - WIDTH_CONSTRAINT_PERCENTAGE)); const LINK_DESTINATION_MEDIA = 'media'; const LINK_DESTINATION_ATTACHMENT = 'attachment'; function getImageSourceUrlBySizeSlug(image, slug) { var _image$media_details, _image$media_details$, _image$media_details$2; // eslint-disable-next-line camelcase return image === null || image === void 0 ? void 0 : (_image$media_details = image.media_details) === null || _image$media_details === void 0 ? void 0 : (_image$media_details$ = _image$media_details.sizes) === null || _image$media_details$ === void 0 ? void 0 : (_image$media_details$2 = _image$media_details$[slug]) === null || _image$media_details$2 === void 0 ? void 0 : _image$media_details$2.source_url; } function attributesFromMedia(_ref) { let { attributes: { linkDestination, href }, setAttributes } = _ref; return media => { if (!media || !media.url) { setAttributes({ mediaAlt: undefined, mediaId: undefined, mediaType: undefined, mediaUrl: undefined, mediaLink: undefined, href: undefined, focalPoint: undefined }); return; } if (isBlobURL(media.url)) { media.type = getBlobTypeByURL(media.url); } let mediaType; let src; // For media selections originated from a file upload. if (media.media_type) { if (media.media_type === 'image') { mediaType = 'image'; } else { // only images and videos are accepted so if the media_type is not an image we can assume it is a video. // video contain the media type of 'file' in the object returned from the rest api. mediaType = 'video'; } } else { // For media selections originated from existing files in the media library. mediaType = media.type; } if (mediaType === 'image') { var _media$sizes, _media$sizes$large, _media$media_details, _media$media_details$, _media$media_details$2; // Try the "large" size URL, falling back to the "full" size URL below. src = ((_media$sizes = media.sizes) === null || _media$sizes === void 0 ? void 0 : (_media$sizes$large = _media$sizes.large) === null || _media$sizes$large === void 0 ? void 0 : _media$sizes$large.url) || ( // eslint-disable-next-line camelcase (_media$media_details = media.media_details) === null || _media$media_details === void 0 ? void 0 : (_media$media_details$ = _media$media_details.sizes) === null || _media$media_details$ === void 0 ? void 0 : (_media$media_details$2 = _media$media_details$.large) === null || _media$media_details$2 === void 0 ? void 0 : _media$media_details$2.source_url); } let newHref = href; if (linkDestination === LINK_DESTINATION_MEDIA) { // Update the media link. newHref = media.url; } // Check if the image is linked to the attachment page. if (linkDestination === LINK_DESTINATION_ATTACHMENT) { // Update the media link. newHref = media.link; } setAttributes({ mediaAlt: media.alt, mediaId: media.id, mediaType, mediaUrl: src || media.url, mediaLink: media.link || undefined, href: newHref, focalPoint: undefined }); }; } function MediaTextEdit(_ref2) { let { attributes, isSelected, setAttributes, clientId } = _ref2; const { focalPoint, href, imageFill, isStackedOnMobile, linkClass, linkDestination, linkTarget, mediaAlt, mediaId, mediaPosition, mediaType, mediaUrl, mediaWidth, rel, verticalAlignment } = attributes; const mediaSizeSlug = attributes.mediaSizeSlug || DEFAULT_MEDIA_SIZE_SLUG; const { imageSizes, image, isContentLocked } = useSelect(select => { var _getSettings; const { __unstableGetContentLockingParent, getSettings } = select(blockEditorStore); return { isContentLocked: !!__unstableGetContentLockingParent(clientId), image: mediaId && isSelected ? select(coreStore).getMedia(mediaId, { context: 'view' }) : null, imageSizes: (_getSettings = getSettings()) === null || _getSettings === void 0 ? void 0 : _getSettings.imageSizes }; }, [isSelected, mediaId, clientId]); const refMediaContainer = useRef(); const imperativeFocalPointPreview = value => { const { style } = refMediaContainer.current.resizable; const { x, y } = value; style.backgroundPosition = `${x * 100}% ${y * 100}%`; }; const [temporaryMediaWidth, setTemporaryMediaWidth] = useState(null); const onSelectMedia = attributesFromMedia({ attributes, setAttributes }); const onSetHref = props => { setAttributes(props); }; const onWidthChange = width => { setTemporaryMediaWidth(applyWidthConstraints(width)); }; const commitWidthChange = width => { setAttributes({ mediaWidth: applyWidthConstraints(width) }); setTemporaryMediaWidth(applyWidthConstraints(width)); }; const classNames = classnames({ 'has-media-on-the-right': 'right' === mediaPosition, 'is-selected': isSelected, 'is-stacked-on-mobile': isStackedOnMobile, [`is-vertically-aligned-${verticalAlignment}`]: verticalAlignment, 'is-image-fill': imageFill }); const widthString = `${temporaryMediaWidth || mediaWidth}%`; const gridTemplateColumns = 'right' === mediaPosition ? `1fr ${widthString}` : `${widthString} 1fr`; const style = { gridTemplateColumns, msGridColumns: gridTemplateColumns }; const onMediaAltChange = newMediaAlt => { setAttributes({ mediaAlt: newMediaAlt }); }; const onVerticalAlignmentChange = alignment => { setAttributes({ verticalAlignment: alignment }); }; const imageSizeOptions = map(filter(imageSizes, _ref3 => { let { slug } = _ref3; return getImageSourceUrlBySizeSlug(image, slug); }), _ref4 => { let { name, slug } = _ref4; return { value: slug, label: name }; }); const updateImage = newMediaSizeSlug => { const newUrl = getImageSourceUrlBySizeSlug(image, newMediaSizeSlug); if (!newUrl) { return null; } setAttributes({ mediaUrl: newUrl, mediaSizeSlug: newMediaSizeSlug }); }; const mediaTextGeneralSettings = createElement(PanelBody, { title: __('Settings') }, createElement(ToggleControl, { label: __('Stack on mobile'), checked: isStackedOnMobile, onChange: () => setAttributes({ isStackedOnMobile: !isStackedOnMobile }) }), mediaType === 'image' && createElement(ToggleControl, { label: __('Crop image to fill entire column'), checked: imageFill, onChange: () => setAttributes({ imageFill: !imageFill }) }), imageFill && mediaUrl && mediaType === 'image' && createElement(FocalPointPicker, { label: __('Focal point picker'), url: mediaUrl, value: focalPoint, onChange: value => setAttributes({ focalPoint: value }), onDragStart: imperativeFocalPointPreview, onDrag: imperativeFocalPointPreview }), mediaType === 'image' && createElement(TextareaControl, { label: __('Alt text (alternative text)'), value: mediaAlt, onChange: onMediaAltChange, help: createElement(Fragment, null, createElement(ExternalLink, { href: "https://www.w3.org/WAI/tutorials/images/decision-tree" }, __('Describe the purpose of the image')), __('Leave empty if the image is purely decorative.')) }), mediaType === 'image' && createElement(ImageSizeControl, { onChangeImage: updateImage, slug: mediaSizeSlug, imageSizeOptions: imageSizeOptions, isResizable: false, imageSizeHelp: __('Select which image size to load.') }), mediaUrl && createElement(RangeControl, { label: __('Media width'), value: temporaryMediaWidth || mediaWidth, onChange: commitWidthChange, min: WIDTH_CONSTRAINT_PERCENTAGE, max: 100 - WIDTH_CONSTRAINT_PERCENTAGE })); const blockProps = useBlockProps({ className: classNames, style }); const innerBlocksProps = useInnerBlocksProps({ className: 'wp-block-media-text__content' }, { template: TEMPLATE }); return createElement(Fragment, null, createElement(InspectorControls, null, mediaTextGeneralSettings), createElement(BlockControls, { group: "block" }, !isContentLocked && createElement(Fragment, null, createElement(BlockVerticalAlignmentControl, { onChange: onVerticalAlignmentChange, value: verticalAlignment }), createElement(ToolbarButton, { icon: pullLeft, title: __('Show media on left'), isActive: mediaPosition === 'left', onClick: () => setAttributes({ mediaPosition: 'left' }) }), createElement(ToolbarButton, { icon: pullRight, title: __('Show media on right'), isActive: mediaPosition === 'right', onClick: () => setAttributes({ mediaPosition: 'right' }) })), mediaType === 'image' && createElement(ImageURLInputUI, { url: href || '', onChangeUrl: onSetHref, linkDestination: linkDestination, mediaType: mediaType, mediaUrl: image && image.source_url, mediaLink: image && image.link, linkTarget: linkTarget, linkClass: linkClass, rel: rel })), createElement("div", blockProps, mediaPosition === 'right' && createElement("div", innerBlocksProps), createElement(MediaContainer, { className: "wp-block-media-text__media", onSelectMedia: onSelectMedia, onWidthChange: onWidthChange, commitWidthChange: commitWidthChange, ref: refMediaContainer, focalPoint, imageFill, isSelected, isStackedOnMobile, mediaAlt, mediaId, mediaPosition, mediaType, mediaUrl, mediaWidth, isContentLocked }), mediaPosition !== 'right' && createElement("div", innerBlocksProps))); } export default MediaTextEdit; //# sourceMappingURL=edit.js.map