diff --git a/package-lock.json b/package-lock.json index 3ee6f318a1da75..9ee5c4f59c5fc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7209,6 +7209,7 @@ "classnames": "^2.2.5", "diff": "^3.5.0", "dom-scroll-into-view": "^1.2.1", + "fast-average-color": "4.3.0", "inherits": "^2.0.3", "lodash": "^4.17.15", "memize": "^1.0.5", @@ -7248,7 +7249,6 @@ "@wordpress/url": "file:packages/url", "@wordpress/viewport": "file:packages/viewport", "classnames": "^2.2.5", - "fast-average-color": "4.3.0", "lodash": "^4.17.15", "memize": "^1.0.5", "moment": "^2.22.1", diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 765b6bc72e4db0..24b1ea1a9402e8 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -45,6 +45,7 @@ "classnames": "^2.2.5", "diff": "^3.5.0", "dom-scroll-into-view": "^1.2.1", + "fast-average-color": "4.3.0", "inherits": "^2.0.3", "lodash": "^4.17.15", "memize": "^1.0.5", diff --git a/packages/block-editor/src/components/background-controls/block-background-ui.js b/packages/block-editor/src/components/background-controls/block-background-ui.js new file mode 100644 index 00000000000000..778a2fcf50b8c0 --- /dev/null +++ b/packages/block-editor/src/components/background-controls/block-background-ui.js @@ -0,0 +1,138 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { useRef } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { + IMAGE_BACKGROUND_TYPE, + VIDEO_BACKGROUND_TYPE, + backgroundImageStyles, + dimRatioToClass, +} from './shared'; +import ResizableArea from './resizable-area'; +import useCoverIsDark from './use-cover-is-dark'; + +export default function BlockBackgroundUi( { + className, + children, + attributes, + isSelected, + toggleSelection, + setAttributes, + overlayColor, + gradientValue, + temporaryMinHeight, + setTemporaryMinHeight, + gradientClass, +} ) { + const { + backgroundType, + dimRatio, + focalPoint, + minHeight, + hasParallax, + url, + } = attributes; + + const style = { + ...( + backgroundType === IMAGE_BACKGROUND_TYPE ? + backgroundImageStyles( url ) : + {} + ), + backgroundColor: overlayColor, + minHeight: ( temporaryMinHeight || minHeight ), + }; + + if ( gradientValue && ! url ) { + style.background = gradientValue; + } + + if ( focalPoint ) { + style.backgroundPosition = `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%`; + } + + const isDarkElement = useRef(); + const isDark = useCoverIsDark( url, dimRatio, overlayColor, isDarkElement ); + + const classes = classnames( + className, + dimRatioToClass( dimRatio ), + { + 'is-dark-theme': isDark, + 'has-background-dim': dimRatio !== 0, + 'has-parallax': hasParallax, + // [ overlayColor.class ]: overlayColor.class, // @TODO fix + 'has-background-gradient': gradientValue, + [ gradientClass ]: ! url && gradientClass, + } + ); + + return ( + toggleSelection( false ) } + onResize={ setTemporaryMinHeight } + onResizeStop={ + ( newMinHeight ) => { + toggleSelection( true ); + setAttributes( { minHeight: newMinHeight } ); + setTemporaryMinHeight( null ); + } + } + > +
+ { IMAGE_BACKGROUND_TYPE === backgroundType && ( + // Used only to programmatically check if the image is dark or not + + ) } + { url && gradientValue && dimRatio !== 0 && ( +
+
+ ); +} diff --git a/packages/block-editor/src/components/background-controls/controls.js b/packages/block-editor/src/components/background-controls/controls.js new file mode 100644 index 00000000000000..e1fcef740437dd --- /dev/null +++ b/packages/block-editor/src/components/background-controls/controls.js @@ -0,0 +1,229 @@ +/** + * WordPress dependencies + */ +import { + FocalPointPicker, + IconButton, + PanelBody, + PanelRow, + RangeControl, + ToggleControl, + Toolbar, + Button, +} from '@wordpress/components'; + +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { + IMAGE_BACKGROUND_TYPE, + VIDEO_BACKGROUND_TYPE, +} from './shared'; +import HeightInput from './height-input'; + +import BlockControls from '../block-controls'; +import InspectorControls from '../inspector-controls'; +import MediaUpload from '../media-upload'; +import MediaUploadCheck from '../media-upload/check'; +import PanelColorSettings from '../panel-color-settings'; +import __experimentalGradientPickerControl from '../gradient-picker/control'; + +const BackgroundToolbarControls = ( { + hasBackground, + onSelectMedia, + mediaId, + allowedMediaTypes, +} ) => ( + + { hasBackground && ( + <> + + + ( + + ) } + /> + + + + ) } + +); + +const BackgroundInspectorControls = ( { + mediaUrl, + backgroundType, + hasParallax, + focalPoint, + setAttributes, + hasBackground, + overlayColor, + setOverlayColor, + gradientValue, + setGradient, + dimRatio, + temporaryMinHeight, + minHeight, +} ) => ( + + + setAttributes( { minHeight: newMinHeight } ) } + /> + + { !! mediaUrl && ( + + { IMAGE_BACKGROUND_TYPE === backgroundType && ( + { + setAttributes( { + hasParallax: ! hasParallax, + ...( ! hasParallax ? { focalPoint: undefined } : {} ), + } ); + } } + /> + ) } + { IMAGE_BACKGROUND_TYPE === backgroundType && ! hasParallax && ( + setAttributes( { focalPoint: newFocalPoint } ) } + /> + ) } + { VIDEO_BACKGROUND_TYPE === backgroundType && ( + + ) } + { hasBackground && ( + <> + { + setAttributes( { + customGradient: undefined, + } ); + setOverlayColor( ...args ); + }, + label: __( 'Overlay Color' ), + } ] } + > + <__experimentalGradientPickerControl + label={ __( 'Overlay Gradient' ) } + onChange={ + ( newGradient ) => { + setGradient( newGradient ); + setAttributes( { + overlayColor: undefined, + } ); + } + } + value={ gradientValue } + /> + { !! mediaUrl && ( + setAttributes( { dimRatio: newDimRation } ) } + min={ 0 } + max={ 100 } + step={ 10 } + required + /> + ) } + + + ) } + +); + +export default function BackgroundControls( { + attributes, + setAttributes, + hasBackground, + overlayColor, + setOverlayColor, + gradientValue, + setGradient, + onSelectMedia, + allowedMediaTypes, + temporaryMinHeight, + showInspectorControls = true, + showToolbarControls = true, +} ) { + const { + backgroundType, + dimRatio, + focalPoint, + hasParallax, + minHeight, + id, + url, + } = attributes; + + return ( + <> + { showInspectorControls && } + { showToolbarControls && } + + ); +} diff --git a/packages/block-editor/src/components/background-controls/default-background-picker.js b/packages/block-editor/src/components/background-controls/default-background-picker.js new file mode 100644 index 00000000000000..28d5786e9c9d87 --- /dev/null +++ b/packages/block-editor/src/components/background-controls/default-background-picker.js @@ -0,0 +1,77 @@ +/** + * WordPress dependencies + */ +import { withNotices } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import BlockIcon from '../block-icon'; +import MediaPlaceholder from '../media-placeholder'; +import __experimentalGradientPicker from '../gradient-picker'; +import ColorPalette from '../color-palette'; + +const DefaultBackgroundPicker = withNotices( ( { + icon, + label, + onSelectMedia, + allowedMediaTypes, + className, + noticeUI, + noticeOperations, + overlayColor, + setOverlayColor, + setGradient, + setAttributes, + gradientValue, +} ) => { + const { removeAllNotices, createErrorNotice } = noticeOperations; + + const placeholderIcon = ; + return ( + <> + { + removeAllNotices(); + createErrorNotice( message ); + } } + > +
+ + <__experimentalGradientPicker + onChange={ + ( newGradient ) => { + setGradient( newGradient ); + setAttributes( { + overlayColor: undefined, + } ); + } + } + value={ gradientValue } + clearable={ false } + /> +
+
+ + ); +} ); + +export default DefaultBackgroundPicker; diff --git a/packages/block-editor/src/components/background-controls/height-input.js b/packages/block-editor/src/components/background-controls/height-input.js new file mode 100644 index 00000000000000..41e97ea7e964e4 --- /dev/null +++ b/packages/block-editor/src/components/background-controls/height-input.js @@ -0,0 +1,47 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { BaseControl } from '@wordpress/components'; +import { withInstanceId } from '@wordpress/compose'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { COVER_MIN_HEIGHT } from './shared'; + +export default withInstanceId( + function( { value = '', instanceId, onChange } ) { + const [ temporaryInput, setTemporaryInput ] = useState( null ); + const inputId = `block-cover-height-input-${ instanceId }`; + return ( + + { + const unprocessedValue = event.target.value; + const inputValue = unprocessedValue !== '' ? + parseInt( event.target.value, 10 ) : + undefined; + if ( ( isNaN( inputValue ) || inputValue < COVER_MIN_HEIGHT ) && inputValue !== undefined ) { + setTemporaryInput( event.target.value ); + return; + } + setTemporaryInput( null ); + onChange( inputValue ); + } } + onBlur={ () => { + if ( temporaryInput !== null ) { + setTemporaryInput( null ); + } + } } + value={ temporaryInput !== null ? temporaryInput : value } + min={ COVER_MIN_HEIGHT } + step="1" + /> + + ); + } +); diff --git a/packages/block-editor/src/components/background-controls/index.js b/packages/block-editor/src/components/background-controls/index.js new file mode 100644 index 00000000000000..3c86828d437c19 --- /dev/null +++ b/packages/block-editor/src/components/background-controls/index.js @@ -0,0 +1 @@ +export { default as __experimentalWithBackgroundControls } from './with-background-controls'; diff --git a/packages/block-editor/src/components/background-controls/on-select-media.js b/packages/block-editor/src/components/background-controls/on-select-media.js new file mode 100644 index 00000000000000..a85f1513d93d1c --- /dev/null +++ b/packages/block-editor/src/components/background-controls/on-select-media.js @@ -0,0 +1,45 @@ +/** + * Internal dependencies + */ +import { + IMAGE_BACKGROUND_TYPE, + VIDEO_BACKGROUND_TYPE, +} from './shared'; + +export default function onSelectMedia( setAttributes ) { + return ( media ) => { + if ( ! media || ! media.url ) { + setAttributes( { url: undefined, id: undefined } ); + return; + } + let mediaType; + // for media selections originated from a file upload. + if ( media.media_type ) { + if ( media.media_type === IMAGE_BACKGROUND_TYPE ) { + mediaType = IMAGE_BACKGROUND_TYPE; + } else { + // only images and videos are accepted so if the media_type is not an image we can assume it is a video. + // Videos contain the media type of 'file' in the object returned from the rest api. + mediaType = VIDEO_BACKGROUND_TYPE; + } + } else { // for media selections originated from existing files in the media library. + if ( + media.type !== IMAGE_BACKGROUND_TYPE && + media.type !== VIDEO_BACKGROUND_TYPE + ) { + return; + } + mediaType = media.type; + } + + setAttributes( { + url: media.url, + id: media.id, + backgroundType: mediaType, + ...( mediaType === VIDEO_BACKGROUND_TYPE ? + { focalPoint: undefined, hasParallax: undefined } : + {} + ), + } ); + }; +} diff --git a/packages/block-editor/src/components/background-controls/resizable-area.js b/packages/block-editor/src/components/background-controls/resizable-area.js new file mode 100644 index 00000000000000..a910366c6d7ca8 --- /dev/null +++ b/packages/block-editor/src/components/background-controls/resizable-area.js @@ -0,0 +1,68 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { ResizableBox } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { + COVER_MIN_HEIGHT, + +} from './shared'; + +const RESIZABLE_BOX_ENABLE_OPTION = { + top: false, + right: false, + bottom: true, + left: false, + topRight: false, + bottomRight: false, + bottomLeft: false, + topLeft: false, +}; + +export default function ResizableArea( { + className, + children, + onResizeStart, + onResize, + onResizeStop, +} ) { + const [ isResizing, setIsResizing ] = useState( false ); + + return ( + { + onResizeStart( elt.clientHeight ); + onResize( elt.clientHeight ); + } } + onResize={ ( event, direction, elt ) => { + onResize( elt.clientHeight ); + if ( ! isResizing ) { + setIsResizing( true ); + } + } } + onResizeStop={ ( event, direction, elt ) => { + onResizeStop( elt.clientHeight ); + setIsResizing( false ); + } } + minHeight={ COVER_MIN_HEIGHT } + > + { children } + + ); +} diff --git a/packages/block-editor/src/components/background-controls/shared.js b/packages/block-editor/src/components/background-controls/shared.js new file mode 100644 index 00000000000000..ec98cc596ad7f8 --- /dev/null +++ b/packages/block-editor/src/components/background-controls/shared.js @@ -0,0 +1,16 @@ +export const IMAGE_BACKGROUND_TYPE = 'image'; +export const VIDEO_BACKGROUND_TYPE = 'video'; +export const COVER_MIN_HEIGHT = 50; +export const ALLOWED_MEDIA_TYPES = [ IMAGE_BACKGROUND_TYPE, VIDEO_BACKGROUND_TYPE ]; + +export function backgroundImageStyles( url ) { + return url ? + { backgroundImage: `url(${ url })` } : + {}; +} + +export function dimRatioToClass( ratio ) { + return ( ratio === 0 || ratio === 50 ) ? + null : + 'has-background-dim-' + ( 10 * Math.round( ratio / 10 ) ); +} diff --git a/packages/block-editor/src/components/background-controls/use-cover-is-dark.js b/packages/block-editor/src/components/background-controls/use-cover-is-dark.js new file mode 100644 index 00000000000000..4e32f101ce5a09 --- /dev/null +++ b/packages/block-editor/src/components/background-controls/use-cover-is-dark.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +import FastAverageColor from 'fast-average-color'; +import tinycolor from 'tinycolor2'; + +/** + * WordPress dependencies + */ +import { + useEffect, + useState, +} from '@wordpress/element'; + +function retrieveFastAverageColor() { + if ( ! retrieveFastAverageColor.fastAverageColor ) { + retrieveFastAverageColor.fastAverageColor = new FastAverageColor(); + } + return retrieveFastAverageColor.fastAverageColor; +} + +/** + * useCoverIsDark is a hook that returns a boolean variable specifying if the cover + * background is dark or not. + * + * @param {?string} url Url of the media background. + * @param {?number} dimRatio Transparency of the overlay color. If an image and + * color are set, dimRatio is used to decide what is used + * for background darkness checking purposes. + * @param {?string} overlayColor String containing the overlay color value if one exists. + * @param {?Object} elementRef If a media background is set, elementRef should contain a reference to a + * dom element that renders that media. + * + * @return {boolean} True if the cover background is considered "dark" and false otherwise. + */ +export default function useCoverIsDark( url, dimRatio = 50, overlayColor, elementRef ) { + const [ isDark, setIsDark ] = useState( false ); + useEffect( () => { + // If opacity is lower than 50 the dominant color is the image or video color, + // so use that color for the dark mode computation. + if ( url && dimRatio <= 50 && elementRef.current ) { + retrieveFastAverageColor().getColorAsync( elementRef.current, ( color ) => { + setIsDark( color.isDark ); + } ); + } + }, [ url, url && dimRatio <= 50 && elementRef.current, setIsDark ] ); + useEffect( () => { + // If opacity is greater than 50 the dominant color is the overlay color, + // so use that color for the dark mode computation. + if ( dimRatio > 50 || ! url ) { + if ( ! overlayColor ) { + // If no overlay color exists the overlay color is black (isDark ) + setIsDark( true ); + return; + } + setIsDark( tinycolor( overlayColor ).isDark() ); + } + }, [ overlayColor, dimRatio > 50 || ! url, setIsDark ] ); + useEffect( () => { + if ( ! url && ! overlayColor ) { + // Reset isDark + setIsDark( false ); + } + }, [ ! url && ! overlayColor, setIsDark ] ); + return isDark; +} diff --git a/packages/block-editor/src/components/background-controls/with-background-controls.js b/packages/block-editor/src/components/background-controls/with-background-controls.js new file mode 100644 index 00000000000000..16367e7c51ffa8 --- /dev/null +++ b/packages/block-editor/src/components/background-controls/with-background-controls.js @@ -0,0 +1,120 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { useCallback, useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { useBlockEditContext } from '../block-edit'; +import { __experimentalUseGradient } from '../gradients'; +import { __experimentalUseColors } from '../colors'; +import BackgroundControls from './controls'; +import { ALLOWED_MEDIA_TYPES } from './shared'; +import DefaultBackgroundPicker from './default-background-picker'; +import BlockBackgroundUi from './block-background-ui'; +import onSelectMedia from './on-select-media'; + +export default function WithBackgroundControls( { + showDefaultPicker, + blockIcon, + blockLabel, + className, + children, + isSelected, +} ) { + const { clientId } = useBlockEditContext(); + const { attributes } = useSelect( + ( select ) => { + const { getBlockAttributes } = select( 'core/block-editor' ); + return { + attributes: getBlockAttributes( clientId ), + }; + }, + [ clientId ] + ); + const { updateBlockAttributes, toggleSelection } = useDispatch( 'core/block-editor' ); + const setAttributes = useCallback( + ( newAttributes ) => updateBlockAttributes( clientId, newAttributes ), + [ updateBlockAttributes, clientId ] + ); + + const { url } = attributes; + + const { + gradientClass, + gradientValue, + setGradient, + } = __experimentalUseGradient(); + + // @TODO: Use Controls from here. + const { OverlayColor } = __experimentalUseColors( [ { + name: 'overlayColor', + property: 'background-color', + } ] ); + + const overlayColor = OverlayColor.color; + const setOverlayColor = OverlayColor.setColor; + // debugger; + + const selectMedia = onSelectMedia( setAttributes ); + + const hasBackground = !! ( url || overlayColor || gradientValue ); + + const [ temporaryMinHeight, setTemporaryMinHeight ] = useState( null ); + + const controls = ; + + if ( ! hasBackground && showDefaultPicker ) { + return ( + <> + { controls } + ; + + ); + } + + const background = ; + + return ( + + { controls } + { background } + + ); +} diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index c7792608518acd..0a92042001d05e 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -2,6 +2,7 @@ * Block Creation Components */ +export * from './background-controls'; export * from './colors'; export * from './gradients'; export * from './font-sizes'; diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 36ce38c1d9c860..11cb836adb5462 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -46,7 +46,6 @@ "@wordpress/url": "file:../url", "@wordpress/viewport": "file:../viewport", "classnames": "^2.2.5", - "fast-average-color": "4.3.0", "lodash": "^4.17.15", "memize": "^1.0.5", "moment": "^2.22.1", diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index d64c80989d8700..f92bc6005d7f36 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -1,66 +1,21 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import FastAverageColor from 'fast-average-color'; -import tinycolor from 'tinycolor2'; - /** * WordPress dependencies */ +// import { compose, withInstanceId } from '@wordpress/compose'; import { - useEffect, - useRef, - useState, -} from '@wordpress/element'; -import { - FocalPointPicker, - IconButton, - PanelBody, - PanelRow, - RangeControl, - ToggleControl, - Toolbar, - withNotices, - ResizableBox, - BaseControl, - Button, -} from '@wordpress/components'; -import { compose, withInstanceId } from '@wordpress/compose'; -import { - BlockControls, - BlockIcon, InnerBlocks, - InspectorControls, - MediaPlaceholder, - MediaUpload, - MediaUploadCheck, - PanelColorSettings, - withColors, - ColorPalette, - __experimentalUseGradient, - __experimentalGradientPickerControl, - __experimentalGradientPicker, + __experimentalWithBackgroundControls, } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; -import { withDispatch } from '@wordpress/data'; /** * Internal dependencies */ import icon from './icon'; -import { - IMAGE_BACKGROUND_TYPE, - VIDEO_BACKGROUND_TYPE, - COVER_MIN_HEIGHT, - backgroundImageStyles, - dimRatioToClass, -} from './shared'; /** * Module Constants */ -const ALLOWED_MEDIA_TYPES = [ 'image', 'video' ]; const INNER_BLOCKS_TEMPLATE = [ [ 'core/paragraph', { align: 'center', @@ -69,504 +24,10 @@ const INNER_BLOCKS_TEMPLATE = [ } ], ]; -function retrieveFastAverageColor() { - if ( ! retrieveFastAverageColor.fastAverageColor ) { - retrieveFastAverageColor.fastAverageColor = new FastAverageColor(); - } - return retrieveFastAverageColor.fastAverageColor; -} - -const CoverHeightInput = withInstanceId( - function( { value = '', instanceId, onChange } ) { - const [ temporaryInput, setTemporaryInput ] = useState( null ); - const inputId = `block-cover-height-input-${ instanceId }`; - return ( - - { - const unprocessedValue = event.target.value; - const inputValue = unprocessedValue !== '' ? - parseInt( event.target.value, 10 ) : - undefined; - if ( ( isNaN( inputValue ) || inputValue < COVER_MIN_HEIGHT ) && inputValue !== undefined ) { - setTemporaryInput( event.target.value ); - return; - } - setTemporaryInput( null ); - onChange( inputValue ); - } } - onBlur={ () => { - if ( temporaryInput !== null ) { - setTemporaryInput( null ); - } - } } - value={ temporaryInput !== null ? temporaryInput : value } - min={ COVER_MIN_HEIGHT } - step="1" - /> - - ); - } -); - -const RESIZABLE_BOX_ENABLE_OPTION = { - top: false, - right: false, - bottom: true, - left: false, - topRight: false, - bottomRight: false, - bottomLeft: false, - topLeft: false, -}; - -function ResizableCover( { - className, - children, - onResizeStart, - onResize, - onResizeStop, -} ) { - const [ isResizing, setIsResizing ] = useState( false ); - - return ( - { - onResizeStart( elt.clientHeight ); - onResize( elt.clientHeight ); - } } - onResize={ ( event, direction, elt ) => { - onResize( elt.clientHeight ); - if ( ! isResizing ) { - setIsResizing( true ); - } - } } - onResizeStop={ ( event, direction, elt ) => { - onResizeStop( elt.clientHeight ); - setIsResizing( false ); - } } - minHeight={ COVER_MIN_HEIGHT } - > - { children } - - ); -} - -function onCoverSelectMedia( setAttributes ) { - return ( media ) => { - if ( ! media || ! media.url ) { - setAttributes( { url: undefined, id: undefined } ); - return; - } - let mediaType; - // for media selections originated from a file upload. - if ( media.media_type ) { - if ( media.media_type === IMAGE_BACKGROUND_TYPE ) { - mediaType = IMAGE_BACKGROUND_TYPE; - } else { - // only images and videos are accepted so if the media_type is not an image we can assume it is a video. - // Videos contain the media type of 'file' in the object returned from the rest api. - mediaType = VIDEO_BACKGROUND_TYPE; - } - } else { // for media selections originated from existing files in the media library. - if ( - media.type !== IMAGE_BACKGROUND_TYPE && - media.type !== VIDEO_BACKGROUND_TYPE - ) { - return; - } - mediaType = media.type; - } - - setAttributes( { - url: media.url, - id: media.id, - backgroundType: mediaType, - ...( mediaType === VIDEO_BACKGROUND_TYPE ? - { focalPoint: undefined, hasParallax: undefined } : - {} - ), - } ); - }; -} - -/** - * useCoverIsDark is a hook that returns a boolean variable specifying if the cover - * background is dark or not. - * - * @param {?string} url Url of the media background. - * @param {?number} dimRatio Transparency of the overlay color. If an image and - * color are set, dimRatio is used to decide what is used - * for background darkness checking purposes. - * @param {?string} overlayColor String containing the overlay color value if one exists. - * @param {?Object} elementRef If a media background is set, elementRef should contain a reference to a - * dom element that renders that media. - * - * @return {boolean} True if the cover background is considered "dark" and false otherwise. - */ -function useCoverIsDark( url, dimRatio = 50, overlayColor, elementRef ) { - const [ isDark, setIsDark ] = useState( false ); - useEffect( () => { - // If opacity is lower than 50 the dominant color is the image or video color, - // so use that color for the dark mode computation. - if ( url && dimRatio <= 50 && elementRef.current ) { - retrieveFastAverageColor().getColorAsync( elementRef.current, ( color ) => { - setIsDark( color.isDark ); - } ); - } - }, [ url, url && dimRatio <= 50 && elementRef.current, setIsDark ] ); - useEffect( () => { - // If opacity is greater than 50 the dominant color is the overlay color, - // so use that color for the dark mode computation. - if ( dimRatio > 50 || ! url ) { - if ( ! overlayColor ) { - // If no overlay color exists the overlay color is black (isDark ) - setIsDark( true ); - return; - } - setIsDark( tinycolor( overlayColor ).isDark() ); - } - }, [ overlayColor, dimRatio > 50 || ! url, setIsDark ] ); - useEffect( () => { - if ( ! url && ! overlayColor ) { - // Reset isDark - setIsDark( false ); - } - }, [ ! url && ! overlayColor, setIsDark ] ); - return isDark; -} - -function CoverEdit( { - attributes, - setAttributes, - isSelected, - className, - noticeUI, - overlayColor, - setOverlayColor, - toggleSelection, - noticeOperations, -} ) { - const { - backgroundType, - dimRatio, - focalPoint, - hasParallax, - id, - minHeight, - url, - } = attributes; - const { - gradientClass, - gradientValue, - setGradient, - } = __experimentalUseGradient(); - const onSelectMedia = onCoverSelectMedia( setAttributes ); - - const toggleParallax = () => { - setAttributes( { - hasParallax: ! hasParallax, - ...( ! hasParallax ? { focalPoint: undefined } : {} ), - } ); - }; - - const isDarkElement = useRef(); - const isDark = useCoverIsDark( url, dimRatio, overlayColor.color, isDarkElement ); - - const [ temporaryMinHeight, setTemporaryMinHeight ] = useState( null ); - - const { removeAllNotices, createErrorNotice } = noticeOperations; - - const style = { - ...( - backgroundType === IMAGE_BACKGROUND_TYPE ? - backgroundImageStyles( url ) : - {} - ), - backgroundColor: overlayColor.color, - minHeight: ( temporaryMinHeight || minHeight ), - }; - - if ( gradientValue && ! url ) { - style.background = gradientValue; - } - - if ( focalPoint ) { - style.backgroundPosition = `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%`; - } - - const hasBackground = !! ( url || overlayColor.color || gradientValue ); - - const controls = ( - <> - - { hasBackground && ( - <> - - - ( - - ) } - /> - - - - ) } - - - { !! url && ( - - { IMAGE_BACKGROUND_TYPE === backgroundType && ( - - ) } - { IMAGE_BACKGROUND_TYPE === backgroundType && ! hasParallax && ( - setAttributes( { focalPoint: newFocalPoint } ) } - /> - ) } - { VIDEO_BACKGROUND_TYPE === backgroundType && ( - - ) } - { hasBackground && ( - <> - - setAttributes( { minHeight: newMinHeight } ) } - /> - - { - setAttributes( { - customGradient: undefined, - } ); - setOverlayColor( ...args ); - }, - label: __( 'Overlay Color' ), - } ] } - > - <__experimentalGradientPickerControl - label={ __( 'Overlay Gradient' ) } - onChange={ - ( newGradient ) => { - setGradient( newGradient ); - setAttributes( { - overlayColor: undefined, - } ); - } - } - value={ gradientValue } - /> - { !! url && ( - setAttributes( { dimRatio: newDimRation } ) } - min={ 0 } - max={ 100 } - step={ 10 } - required - /> - ) } - - - ) } - - - ); - - if ( ! hasBackground ) { - const placeholderIcon = ; - const label = __( 'Cover' ); - - return ( - <> - { controls } - { - removeAllNotices(); - createErrorNotice( message ); - } } - > -
- - <__experimentalGradientPicker - onChange={ - ( newGradient ) => { - setGradient( newGradient ); - setAttributes( { - overlayColor: undefined, - } ); - } - } - value={ gradientValue } - clearable={ false } - /> -
-
- - ); - } - - const classes = classnames( - className, - dimRatioToClass( dimRatio ), - { - 'is-dark-theme': isDark, - 'has-background-dim': dimRatio !== 0, - 'has-parallax': hasParallax, - [ overlayColor.class ]: overlayColor.class, - 'has-background-gradient': gradientValue, - [ gradientClass ]: ! url && gradientClass, - } - ); - - return ( - <> - { controls } - toggleSelection( false ) } - onResize={ setTemporaryMinHeight } - onResizeStop={ - ( newMinHeight ) => { - toggleSelection( true ); - setAttributes( { minHeight: newMinHeight } ); - setTemporaryMinHeight( null ); - } - } - > - -
- { IMAGE_BACKGROUND_TYPE === backgroundType && ( - // Used only to programmatically check if the image is dark or not - - ) } - { url && gradientValue && dimRatio !== 0 && ( -
-
- - ); -} - -export default compose( [ - withDispatch( ( dispatch ) => { - const { toggleSelection } = dispatch( 'core/block-editor' ); - - return { - toggleSelection, - }; - } ), - withColors( { overlayColor: 'background-color' } ), - withNotices, - withInstanceId, -] )( CoverEdit ); +export default ( props ) => __experimentalWithBackgroundControls( { + showDefaultPicker: true, + blockIcon: icon, + blockLabel: __( 'Cover' ), + children: , + ...props, +} );