diff --git a/package-lock.json b/package-lock.json index 5c30b5addb71b0..82560b53523e71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2563,6 +2563,7 @@ "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/viewport": "file:packages/viewport", "classnames": "^2.2.5", + "fast-average-color": "4.3.0", "lodash": "^4.17.11", "memize": "^1.0.5", "url": "^0.11.0" @@ -8190,6 +8191,11 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, + "fast-average-color": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-4.3.0.tgz", + "integrity": "sha512-k8FXd6+JeXoItmdNqB3hMwFgArryjdYBLuzEM8fRY/oztd/051yhSHU6GUrMOfIQU9dDHyFDcIAkGrQKlYtpDA==" + }, "fast-deep-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 8c5ebfa5cb6dc1..5a497ce617376f 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -38,6 +38,7 @@ "@wordpress/keycodes": "file:../keycodes", "@wordpress/viewport": "file:../viewport", "classnames": "^2.2.5", + "fast-average-color": "4.3.0", "lodash": "^4.17.11", "memize": "^1.0.5", "url": "^0.11.0" diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js new file mode 100644 index 00000000000000..398e842d9eafbd --- /dev/null +++ b/packages/block-library/src/cover/edit.js @@ -0,0 +1,367 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import FastAverageColor from 'fast-average-color'; +import tinycolor from 'tinycolor2'; + +/** + * WordPress dependencies + */ +import { + FocalPointPicker, + IconButton, + PanelBody, + RangeControl, + ToggleControl, + Toolbar, + withNotices, +} from '@wordpress/components'; +import { compose } from '@wordpress/compose'; +import { + BlockControls, + BlockIcon, + InnerBlocks, + InspectorControls, + MediaPlaceholder, + MediaUpload, + MediaUploadCheck, + PanelColorSettings, + withColors, +} from '@wordpress/editor'; +import { Component, createRef, Fragment } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import icon from './icon'; + +/** + * Module Constants + */ +export const IMAGE_BACKGROUND_TYPE = 'image'; +export const VIDEO_BACKGROUND_TYPE = 'video'; +const ALLOWED_MEDIA_TYPES = [ 'image', 'video' ]; +const INNER_BLOCKS_TEMPLATE = [ + [ 'core/paragraph', { + align: 'center', + fontSize: 'large', + placeholder: __( 'Write title…' ), + } ], +]; +const INNER_BLOCKS_ALLOWED_BLOCKS = [ 'core/button', 'core/heading', 'core/paragraph' ]; + +function retrieveFastAverageColor() { + if ( ! retrieveFastAverageColor.fastAverageColor ) { + retrieveFastAverageColor.fastAverageColor = new FastAverageColor(); + } + return retrieveFastAverageColor.fastAverageColor; +} + +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 ) ); +} + +class CoverEdit extends Component { + constructor() { + super( ...arguments ); + this.state = { + isDark: false, + }; + this.imageRef = createRef(); + this.videoRef = createRef(); + this.changeIsDarkIfRequired = this.changeIsDarkIfRequired.bind( this ); + } + + componentDidMount() { + this.handleBackgroundMode(); + } + + componentDidUpdate( prevProps ) { + this.handleBackgroundMode( prevProps ); + } + + render() { + const { + attributes, + setAttributes, + className, + noticeOperations, + noticeUI, + overlayColor, + setOverlayColor, + } = this.props; + const { + backgroundType, + dimRatio, + focalPoint, + hasParallax, + id, + url, + } = attributes; + const onSelectMedia = ( 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, + } ); + }; + const toggleParallax = () => setAttributes( { hasParallax: ! hasParallax } ); + const setDimRatio = ( ratio ) => setAttributes( { dimRatio: ratio } ); + + const style = { + ...( + backgroundType === IMAGE_BACKGROUND_TYPE ? + backgroundImageStyles( url ) : + {} + ), + backgroundColor: overlayColor.color, + }; + + if ( focalPoint ) { + style.backgroundPosition = `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%`; + } + + const controls = ( + + + { !! url && ( + + + + ( + + ) } + /> + + + + ) } + + { !! url && ( + + + { IMAGE_BACKGROUND_TYPE === backgroundType && ( + + ) } + { IMAGE_BACKGROUND_TYPE === backgroundType && ! hasParallax && ( + setAttributes( { focalPoint: value } ) } + /> + ) } + + + + + + ) } + + ); + + if ( ! url ) { + const placeholderIcon = ; + const label = __( 'Cover' ); + + return ( + + { controls } + + + ); + } + + const classes = classnames( + className, + dimRatioToClass( dimRatio ), + { + 'is-dark-theme': this.state.isDark, + 'has-background-dim': dimRatio !== 0, + 'has-parallax': hasParallax, + } + ); + + return ( + + { controls } +
+ { IMAGE_BACKGROUND_TYPE === backgroundType && ( + // Used only to programmatically check if the image is dark or not + + ) } + { VIDEO_BACKGROUND_TYPE === backgroundType && ( +
+
+ ); + } + + handleBackgroundMode( prevProps ) { + const { attributes, overlayColor } = this.props; + const { dimRatio, url } = attributes; + // 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 ) { + if ( + prevProps && + prevProps.attributes.dimRatio > 50 && + prevProps.overlayColor.color === overlayColor.color + ) { + // No relevant prop changes happened there is no need to apply any change. + return; + } + if ( ! overlayColor.color ) { + // If no overlay color exists the overlay color is black (isDark ) + this.changeIsDarkIfRequired( true ); + return; + } + this.changeIsDarkIfRequired( + tinycolor( overlayColor.color ).isDark() + ); + return; + } + // 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 ( + prevProps && + prevProps.attributes.dimRatio <= 50 && + prevProps.attributes.url === url + ) { + // No relevant prop changes happened there is no need to apply any change. + return; + } + const { backgroundType } = attributes; + + let element; + + switch ( backgroundType ) { + case IMAGE_BACKGROUND_TYPE: + element = this.imageRef.current; + break; + case VIDEO_BACKGROUND_TYPE: + element = this.videoRef.current; + break; + } + if ( ! element ) { + return; + } + retrieveFastAverageColor().getColorAsync( element, ( color ) => { + this.changeIsDarkIfRequired( color.isDark ); + } ); + } + + changeIsDarkIfRequired( newIsDark ) { + if ( this.state.isDark !== newIsDark ) { + this.setState( { + isDark: newIsDark, + } ); + } + } +} + +export default compose( [ + withColors( { overlayColor: 'background-color' } ), + withNotices, +] )( CoverEdit ); diff --git a/packages/block-library/src/cover/index.js b/packages/block-library/src/cover/index.js index d8fba7eb6e7cb6..cd39eaf9e496a5 100644 --- a/packages/block-library/src/cover/index.js +++ b/packages/block-library/src/cover/index.js @@ -9,35 +9,23 @@ import classnames from 'classnames'; */ import { createBlock } from '@wordpress/blocks'; import { - FocalPointPicker, - IconButton, - PanelBody, - RangeControl, - ToggleControl, - Toolbar, - withNotices, -} from '@wordpress/components'; -import { compose } from '@wordpress/compose'; -import { - BlockControls, - BlockIcon, InnerBlocks, - InspectorControls, - MediaPlaceholder, - MediaUpload, - MediaUploadCheck, - PanelColorSettings, RichText, getColorClassName, - withColors, } from '@wordpress/editor'; -import { Fragment } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import icon from './icon'; +import { + default as CoverEdit, + IMAGE_BACKGROUND_TYPE, + VIDEO_BACKGROUND_TYPE, + backgroundImageStyles, + dimRatioToClass, +} from './edit'; const blockAttributes = { url: { @@ -71,18 +59,6 @@ const blockAttributes = { export const name = 'core/cover'; -const ALLOWED_MEDIA_TYPES = [ 'image', 'video' ]; -const IMAGE_BACKGROUND_TYPE = 'image'; -const VIDEO_BACKGROUND_TYPE = 'video'; -const INNER_BLOCKS_TEMPLATE = [ - [ 'core/paragraph', { - align: 'center', - fontSize: 'large', - placeholder: __( 'Write title…' ), - } ], -]; -const INNER_BLOCKS_ALLOWED_BLOCKS = [ 'core/button', 'core/heading', 'core/paragraph' ]; - export const settings = { title: __( 'Cover' ), @@ -160,195 +136,6 @@ export const settings = { ], }, - edit: compose( [ - withColors( { overlayColor: 'background-color' } ), - withNotices, - ] )( - ( { attributes, setAttributes, className, noticeOperations, noticeUI, overlayColor, setOverlayColor } ) => { - const { - backgroundType, - dimRatio, - focalPoint, - hasParallax, - id, - url, - } = attributes; - const onSelectMedia = ( 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, - } ); - }; - const toggleParallax = () => setAttributes( { hasParallax: ! hasParallax } ); - const setDimRatio = ( ratio ) => setAttributes( { dimRatio: ratio } ); - - const style = { - ...( - backgroundType === IMAGE_BACKGROUND_TYPE ? - backgroundImageStyles( url ) : - {} - ), - backgroundColor: overlayColor.color, - }; - - if ( focalPoint ) { - style.backgroundPosition = `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%`; - } - - const controls = ( - - - { !! url && ( - - - - ( - - ) } - /> - - - - ) } - - { !! url && ( - - - { IMAGE_BACKGROUND_TYPE === backgroundType && ( - - ) } - { IMAGE_BACKGROUND_TYPE === backgroundType && ! hasParallax && ( - setAttributes( { focalPoint: value } ) } - /> - ) } - - - - - - ) } - - ); - - if ( ! url ) { - const placeholderIcon = ; - const label = __( 'Cover' ); - - return ( - - { controls } - - - ); - } - - const classes = classnames( - className, - dimRatioToClass( dimRatio ), - { - 'has-background-dim': dimRatio !== 0, - 'has-parallax': hasParallax, - } - ); - - return ( - - { controls } -
- { VIDEO_BACKGROUND_TYPE === backgroundType && ( -
-
- ); - } - ), - save( { attributes } ) { const { backgroundType, @@ -395,6 +182,7 @@ export const settings = { ); }, + edit: CoverEdit, deprecated: [ { attributes: { ...blockAttributes, @@ -564,15 +352,3 @@ export const settings = { }, } ], }; - -function dimRatioToClass( ratio ) { - return ( ratio === 0 || ratio === 50 ) ? - null : - 'has-background-dim-' + ( 10 * Math.round( ratio / 10 ) ); -} - -function backgroundImageStyles( url ) { - return url ? - { backgroundImage: `url(${ url })` } : - {}; -} diff --git a/packages/editor/src/components/block-mover/style.scss b/packages/editor/src/components/block-mover/style.scss index 39a908806ad5b9..be9ddeab9c1b13 100644 --- a/packages/editor/src/components/block-mover/style.scss +++ b/packages/editor/src/components/block-mover/style.scss @@ -43,7 +43,8 @@ } // Nested movers have a background, so don't invert the colors there. - .is-dark-theme .wp-block .wp-block & { + .is-dark-theme .wp-block .wp-block &, + .wp-block .is-dark-theme .wp-block & { color: $dark-opacity-300; } @@ -79,7 +80,8 @@ } // Nested movers have a background, so don't invert the colors there. - .is-dark-theme .wp-block .wp-block & { + .is-dark-theme .wp-block .wp-block &, + .wp-block .is-dark-theme .wp-block & { color: $dark-opacity-500; } }