UNPKG

@wordpress/block-library

Version:
468 lines (430 loc) 17.3 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import { createElement, Fragment } from "@wordpress/element"; /** * External dependencies */ import { View, TouchableWithoutFeedback, InteractionManager, AccessibilityInfo, Platform } from 'react-native'; import Video from 'react-native-video'; import classnames from 'classnames/dedupe'; /** * WordPress dependencies */ import { requestImageFailedRetryDialog, requestImageUploadCancelDialog, requestImageFullscreenPreview, mediaUploadSync } from '@wordpress/react-native-bridge'; import { __ } from '@wordpress/i18n'; import { Icon, Image, ImageEditingButton, IMAGE_DEFAULT_FOCAL_POINT, ToolbarButton, Gradient, ColorPalette, ColorPicker, BottomSheetConsumer, useConvertUnitToMobile, useMobileGlobalStylesColors } from '@wordpress/components'; import { BlockControls, InnerBlocks, InspectorControls, MEDIA_TYPE_IMAGE, MediaPlaceholder, MediaUpload, MediaUploadProgress, getColorObjectByColorValue, getColorObjectByAttributeValues, getGradientValueBySlug, store as blockEditorStore } from '@wordpress/block-editor'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { useDispatch, withSelect, withDispatch } from '@wordpress/data'; import { useEffect, useState, useRef, useCallback, useMemo } from '@wordpress/element'; import { cover as icon, replace, image, warning } from '@wordpress/icons'; import { getProtocol } from '@wordpress/url'; import { store as editPostStore } from '@wordpress/edit-post'; /** * Internal dependencies */ import styles from './style.scss'; import { attributesFromMedia, ALLOWED_MEDIA_TYPES, IMAGE_BACKGROUND_TYPE, VIDEO_BACKGROUND_TYPE, COVER_DEFAULT_HEIGHT } from './shared'; import Controls from './controls'; import useCoverIsDark from './use-cover-is-dark'; /** * Constants */ const INNER_BLOCKS_TEMPLATE = [['core/paragraph', { align: 'center', placeholder: __('Write title…') }]]; const Cover = _ref => { var _style$color, _styles$overlay; let { attributes, getStylesFromColorScheme, isParentSelected, onFocus, setAttributes, openGeneralSidebar, closeSettingsBottomSheet, isSelected, selectBlock, blockWidth, hasInnerBlocks } = _ref; const { backgroundType, dimRatio, focalPoint, minHeight, url, id, style, customOverlayColor, minHeightUnit = 'px', allowedBlocks, templateLock, customGradient, gradient, overlayColor, isDark } = attributes; const [isScreenReaderEnabled, setIsScreenReaderEnabled] = useState(false); useEffect(() => { let isCurrent = true; // Sync with local media store. mediaUploadSync(); const a11yInfoChangeSubscription = AccessibilityInfo.addEventListener('screenReaderChanged', setIsScreenReaderEnabled); AccessibilityInfo.isScreenReaderEnabled().then(() => { if (isCurrent) { setIsScreenReaderEnabled(); } }); return () => { isCurrent = false; a11yInfoChangeSubscription.remove(); }; }, []); const convertedMinHeight = useConvertUnitToMobile(minHeight || COVER_DEFAULT_HEIGHT, minHeightUnit); const isImage = backgroundType === MEDIA_TYPE_IMAGE; const THEME_COLORS_COUNT = 4; const colorsDefault = useMobileGlobalStylesColors(); const coverDefaultPalette = useMemo(() => { return { colors: colorsDefault.slice(0, THEME_COLORS_COUNT) }; }, [colorsDefault]); const gradients = useMobileGlobalStylesColors('gradients'); const gradientValue = customGradient || getGradientValueBySlug(gradients, gradient); const overlayColorValue = getColorObjectByAttributeValues(colorsDefault, overlayColor); const hasBackground = !!(url || style && style.color && style.color.background || attributes.overlayColor || overlayColorValue.color || customOverlayColor || gradientValue); const hasOnlyColorBackground = !url && (hasBackground || hasInnerBlocks); const [isCustomColorPickerShowing, setCustomColorPickerShowing] = useState(false); const openMediaOptionsRef = useRef(); // Initialize uploading flag to false, awaiting sync. const [isUploadInProgress, setIsUploadInProgress] = useState(false); // Initialize upload failure flag to true if url is local. const [didUploadFail, setDidUploadFail] = useState(id && getProtocol(url) === 'file:'); // Don't show failure if upload is in progress. const shouldShowFailure = didUploadFail && !isUploadInProgress; const onSelectMedia = media => { setDidUploadFail(false); const onSelect = attributesFromMedia(setAttributes, dimRatio); onSelect(media); }; const onMediaPressed = () => { if (isUploadInProgress) { requestImageUploadCancelDialog(id); } else if (shouldShowFailure) { requestImageFailedRetryDialog(id); } else if (isImage && url) { requestImageFullscreenPreview(url); } }; const [isVideoLoading, setIsVideoLoading] = useState(true); const onVideoLoadStart = () => { setIsVideoLoading(true); }; const onVideoLoad = () => { setIsVideoLoading(false); }; const onClearMedia = useCallback(() => { setAttributes({ focalPoint: undefined, hasParallax: undefined, id: undefined, url: undefined }); closeSettingsBottomSheet(); }, [closeSettingsBottomSheet]); function setColor(color) { var _colorValue$slug, _ref2; const colorValue = getColorObjectByColorValue(colorsDefault, color); setAttributes({ // Clear all related attributes (only one should be set). overlayColor: (_colorValue$slug = colorValue === null || colorValue === void 0 ? void 0 : colorValue.slug) !== null && _colorValue$slug !== void 0 ? _colorValue$slug : undefined, customOverlayColor: (_ref2 = !(colorValue !== null && colorValue !== void 0 && colorValue.slug) && color) !== null && _ref2 !== void 0 ? _ref2 : undefined, gradient: undefined, customGradient: undefined }); } function openColorPicker() { selectBlock(); setCustomColorPickerShowing(true); openGeneralSidebar(); } const { __unstableMarkNextChangeAsNotPersistent } = useDispatch(blockEditorStore); const isCoverDark = useCoverIsDark(isDark, url, dimRatio, overlayColorValue === null || overlayColorValue === void 0 ? void 0 : overlayColorValue.color); useEffect(() => { var _attributes$className; // This side-effect should not create an undo level. __unstableMarkNextChangeAsNotPersistent(); // Used to set a default color for its InnerBlocks // since there's no system to inherit styles yet // the RichText component will check if there are // parent styles for the current block. If there are, // it will use that color instead. setAttributes({ isDark: isCoverDark, childrenStyles: isCoverDark ? styles.defaultColor : styles.defaultColorLightMode }); // Ensure that "is-light" is removed from "className" attribute if cover background is dark. if (isCoverDark && (_attributes$className = attributes.className) !== null && _attributes$className !== void 0 && _attributes$className.includes('is-light')) { const className = classnames(attributes.className, { 'is-light': false }); setAttributes({ className: className !== '' ? className : undefined }); } }, [isCoverDark]); const backgroundColor = getStylesFromColorScheme(styles.backgroundSolid, styles.backgroundSolidDark); const overlayStyles = [styles.overlay, url && { opacity: dimRatio / 100 }, !gradientValue && { backgroundColor: customOverlayColor || (overlayColorValue === null || overlayColorValue === void 0 ? void 0 : overlayColorValue.color) || (style === null || style === void 0 ? void 0 : (_style$color = style.color) === null || _style$color === void 0 ? void 0 : _style$color.background) || ((_styles$overlay = styles.overlay) === null || _styles$overlay === void 0 ? void 0 : _styles$overlay.color) }, // While we don't support theme colors we add a default bg color. !overlayColorValue.color && !url ? backgroundColor : {}, isImage && isParentSelected && !isUploadInProgress && !didUploadFail && styles.overlaySelected]; const placeholderIconStyle = getStylesFromColorScheme(styles.icon, styles.iconDark); const placeholderIcon = createElement(Icon, _extends({ icon: icon }, placeholderIconStyle)); const toolbarControls = open => createElement(BlockControls, { group: "other" }, createElement(ToolbarButton, { title: __('Edit cover media'), icon: replace, onClick: open })); const accessibilityHint = Platform.OS === 'ios' ? __('Double tap to open Action Sheet to add image or video') : __('Double tap to open Bottom Sheet to add image or video'); const addMediaButton = () => createElement(TouchableWithoutFeedback, { accessibilityHint: accessibilityHint, accessibilityLabel: __('Add image or video'), accessibilityRole: "button", onPress: openMediaOptionsRef.current }, createElement(View, { style: styles.selectImageContainer }, createElement(View, { style: styles.selectImage }, createElement(Icon, _extends({ size: 16, icon: image }, styles.selectImageIcon))))); const onBottomSheetClosed = useCallback(() => { InteractionManager.runAfterInteractions(() => { setCustomColorPickerShowing(false); }); }, []); const colorPickerControls = createElement(InspectorControls, null, createElement(BottomSheetConsumer, null, _ref3 => { let { shouldEnableBottomSheetScroll, shouldEnableBottomSheetMaxHeight, onHandleClosingBottomSheet, onHandleHardwareButtonPress, isBottomSheetContentScrolling } = _ref3; return createElement(ColorPicker, { shouldEnableBottomSheetScroll: shouldEnableBottomSheetScroll, shouldEnableBottomSheetMaxHeight: shouldEnableBottomSheetMaxHeight, setColor: setColor, onNavigationBack: closeSettingsBottomSheet, onHandleClosingBottomSheet: onHandleClosingBottomSheet, onHandleHardwareButtonPress: onHandleHardwareButtonPress, onBottomSheetClosed: onBottomSheetClosed, isBottomSheetContentScrolling: isBottomSheetContentScrolling, bottomLabelText: __('Select a color') }); })); const renderContent = getMediaOptions => createElement(Fragment, null, renderBackground(getMediaOptions), isParentSelected && hasOnlyColorBackground && addMediaButton()); const renderBackground = getMediaOptions => { var _styles$image; return createElement(TouchableWithoutFeedback, { accessible: !isParentSelected, onPress: onMediaPressed, disabled: !isParentSelected }, createElement(View, { style: [styles.background, backgroundColor] }, getMediaOptions(), isParentSelected && backgroundType === VIDEO_BACKGROUND_TYPE && toolbarControls(openMediaOptionsRef.current), createElement(MediaUploadProgress, { mediaId: id, onUpdateMediaProgress: () => { setIsUploadInProgress(true); }, onFinishMediaUploadWithSuccess: _ref4 => { let { mediaServerId, mediaUrl } = _ref4; setIsUploadInProgress(false); setDidUploadFail(false); setAttributes({ id: mediaServerId, url: mediaUrl, backgroundType }); }, onFinishMediaUploadWithFailure: () => { setIsUploadInProgress(false); setDidUploadFail(true); }, onMediaUploadStateReset: () => { setIsUploadInProgress(false); setDidUploadFail(false); setAttributes({ id: undefined, url: undefined }); } }), IMAGE_BACKGROUND_TYPE === backgroundType && createElement(View, { style: styles.imageContainer }, createElement(Image, { editButton: false, focalPoint: focalPoint || IMAGE_DEFAULT_FOCAL_POINT, isSelected: isParentSelected, isUploadFailed: didUploadFail, isUploadInProgress: isUploadInProgress, onSelectMediaUploadOption: onSelectMedia, openMediaOptions: openMediaOptionsRef.current, url: url, width: (_styles$image = styles.image) === null || _styles$image === void 0 ? void 0 : _styles$image.width })), VIDEO_BACKGROUND_TYPE === backgroundType && createElement(Video, { muted: true, disableFocus: true, repeat: true, resizeMode: 'cover', source: { uri: url }, onLoad: onVideoLoad, onLoadStart: onVideoLoadStart, style: [styles.background, // Hide Video component since it has black background while loading the source. { opacity: isVideoLoading ? 0 : 1 }] }))); }; if (!hasBackground && !hasInnerBlocks || isCustomColorPickerShowing) { var _styles$mediaPlacehol; return createElement(View, null, isCustomColorPickerShowing && colorPickerControls, createElement(MediaPlaceholder, { height: (_styles$mediaPlacehol = styles.mediaPlaceholderEmptyStateContainer) === null || _styles$mediaPlacehol === void 0 ? void 0 : _styles$mediaPlacehol.height, backgroundColor: customOverlayColor, hideContent: customOverlayColor !== '' && customOverlayColor !== undefined, icon: placeholderIcon, labels: { title: __('Cover') }, onSelect: onSelectMedia, allowedTypes: ALLOWED_MEDIA_TYPES, onFocus: onFocus }, createElement(View, { style: styles.colorPaletteWrapper, pointerEvents: isScreenReaderEnabled ? 'none' : 'auto' }, createElement(BottomSheetConsumer, null, _ref5 => { let { shouldEnableBottomSheetScroll } = _ref5; return createElement(ColorPalette, { customColorIndicatorStyles: styles.paletteColorIndicator, customIndicatorWrapperStyles: styles.paletteCustomIndicatorWrapper, setColor: setColor, onCustomPress: openColorPicker, defaultSettings: coverDefaultPalette, shouldShowCustomLabel: false, shouldShowCustomVerticalSeparator: false, shouldEnableBottomSheetScroll: shouldEnableBottomSheetScroll }); })))); } return createElement(View, { style: styles.backgroundContainer }, isSelected && createElement(InspectorControls, null, createElement(Controls, { attributes: attributes, didUploadFail: didUploadFail, hasOnlyColorBackground: hasOnlyColorBackground, isUploadInProgress: isUploadInProgress, onClearMedia: onClearMedia, onSelectMedia: onSelectMedia, setAttributes: setAttributes })), createElement(View, { pointerEvents: "box-none", style: [styles.content, { minHeight: convertedMinHeight }] }, createElement(InnerBlocks, { allowedBlocks: allowedBlocks, template: INNER_BLOCKS_TEMPLATE, templateLock: templateLock, templateInsertUpdatesSelection: true, blockWidth: blockWidth })), createElement(View, { pointerEvents: "none", style: styles.overlayContainer }, createElement(View, { style: overlayStyles }, gradientValue && createElement(Gradient, { gradientValue: gradientValue, style: styles.background }))), createElement(MediaUpload, { allowedTypes: ALLOWED_MEDIA_TYPES, isReplacingMedia: !hasOnlyColorBackground, onSelect: onSelectMedia, render: _ref6 => { let { open, getMediaOptions } = _ref6; openMediaOptionsRef.current = open; return renderContent(getMediaOptions); } }), isImage && url && openMediaOptionsRef.current && isParentSelected && !isUploadInProgress && !didUploadFail && createElement(View, { style: styles.imageEditButton }, createElement(ImageEditingButton, { onSelectMediaUploadOption: onSelectMedia, openMediaOptions: openMediaOptionsRef.current, pickerOptions: [{ destructiveButton: true, id: 'clearMedia', label: __('Clear Media'), onPress: onClearMedia, separated: true, value: 'clearMedia' }], url: url })), shouldShowFailure && createElement(View, { pointerEvents: "none", style: styles.uploadFailedContainer }, createElement(View, { style: styles.uploadFailed }, createElement(Icon, _extends({ icon: warning }, styles.uploadFailedIcon))))); }; export default compose([withSelect((select, _ref7) => { var _getBlock; let { clientId } = _ref7; const { getSelectedBlockClientId, getBlock } = select(blockEditorStore); const selectedBlockClientId = getSelectedBlockClientId(); const { getSettings } = select(blockEditorStore); const hasInnerBlocks = ((_getBlock = getBlock(clientId)) === null || _getBlock === void 0 ? void 0 : _getBlock.innerBlocks.length) > 0; return { settings: getSettings(), isParentSelected: selectedBlockClientId === clientId, hasInnerBlocks }; }), withDispatch((dispatch, _ref8) => { let { clientId } = _ref8; const { openGeneralSidebar } = dispatch(editPostStore); const { selectBlock } = dispatch(blockEditorStore); return { openGeneralSidebar: () => openGeneralSidebar('edit-post/block'), closeSettingsBottomSheet() { dispatch(editPostStore).closeGeneralSidebar(); }, selectBlock: () => selectBlock(clientId) }; }), withPreferredColorScheme])(Cover); //# sourceMappingURL=edit.native.js.map