diff --git a/packages/block-library/src/pullquote/edit.js b/packages/block-library/src/pullquote/edit.js new file mode 100644 index 0000000000000..0f042f5e0fa0e --- /dev/null +++ b/packages/block-library/src/pullquote/edit.js @@ -0,0 +1,145 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { includes, map } from 'lodash'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + Component, + Fragment, +} from '@wordpress/element'; +import { + RichText, + ContrastChecker, + InspectorControls, + withColors, + PanelColorSettings, +} from '@wordpress/editor'; + +export const SOLID_COLOR_STYLE_NAME = 'solid-color'; +export const SOLID_COLOR_CLASS = `is-style-${ SOLID_COLOR_STYLE_NAME }`; + +export const toRichTextValue = ( value ) => map( value, ( ( subValue ) => subValue.children ) ); +export const fromRichTextValue = ( value ) => map( value, ( subValue ) => ( { + children: subValue, +} ) ); + +class PullQuoteEdit extends Component { + constructor( props ) { + super( props ); + + this.wasTextColorAutomaticallyComputed = false; + this.pullQuoteMainColorSetter = this.pullQuoteMainColorSetter.bind( this ); + this.pullQuoteTextColorSetter = this.pullQuoteTextColorSetter.bind( this ); + } + + pullQuoteMainColorSetter( colorValue ) { + const { colorUtils, textColor, setTextColor, setMainColor } = this.props; + setMainColor( colorValue ); + if ( ! textColor.color || this.wasTextColorAutomaticallyComputed ) { + this.wasTextColorAutomaticallyComputed = true; + setTextColor( colorUtils.getMostReadableColor( colorValue ) ); + } + } + + pullQuoteTextColorSetter( colorValue ) { + const { setTextColor } = this.props; + setTextColor( colorValue ); + this.wasTextColorAutomaticallyComputed = false; + } + + render() { + const { + attributes, + mainColor, + textColor, + setAttributes, + isSelected, + className, + } = this.props; + + const { value, citation } = attributes; + + const isSolidColorStyle = includes( className, SOLID_COLOR_CLASS ); + const figureStyle = isSolidColorStyle ? + { backgroundColor: mainColor.color } : + { borderColor: mainColor.color }; + const blockquoteStyle = { + color: textColor.color, + }; + const blockquoteClasses = textColor.color ? classnames( 'has-text-color', { + [ textColor.class ]: textColor.class, + } ) : undefined; + return ( + +
+
+ setAttributes( { + value: fromRichTextValue( nextValue ), + } ) + } + /* translators: the text of the quotation */ + placeholder={ __( 'Write quote…' ) } + wrapperClassName="block-library-pullquote__content" + /> + { ( ! RichText.isEmpty( citation ) || isSelected ) && ( + setAttributes( { + citation: nextCitation, + } ) + } + className="wp-block-pullquote__citation" + /> + ) } +
+
+ + + { isSolidColorStyle && ( + + ) } + + +
+ ); + } +} + +export default withColors( { mainColor: 'background-color', textColor: 'color' } )( + PullQuoteEdit +); diff --git a/packages/block-library/src/pullquote/editor.scss b/packages/block-library/src/pullquote/editor.scss index 075f2ff5aa56f..a05314749bf98 100644 --- a/packages/block-library/src/pullquote/editor.scss +++ b/packages/block-library/src/pullquote/editor.scss @@ -22,7 +22,25 @@ & blockquote > .block-library-pullquote__content .editor-rich-text__tinymce[data-is-empty="true"]::before, & blockquote > .editor-rich-text p { - font-size: 24px; + font-size: 28px; line-height: 1.6; } } + +.wp-block-pullquote.is-style-solid-color { + margin-left: 0; + margin-right: 0; + + & blockquote > .editor-rich-text p { + font-size: 32px; + } + + .wp-block-pullquote__citation { + text-transform: none; + font-style: normal; + } +} + +.wp-block-pullquote .wp-block-pullquote__citation { + color: inherit; +} diff --git a/packages/block-library/src/pullquote/index.js b/packages/block-library/src/pullquote/index.js index bd4ac84feb8d8..1c611138ee477 100644 --- a/packages/block-library/src/pullquote/index.js +++ b/packages/block-library/src/pullquote/index.js @@ -1,20 +1,29 @@ /** * External dependencies */ -import { map } from 'lodash'; +import classnames from 'classnames'; +import { get, includes } from 'lodash'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { + getColorClassName, RichText, + getColorObjectByAttributeValues, } from '@wordpress/editor'; +import { + select, +} from '@wordpress/data'; + +import { + default as edit, + SOLID_COLOR_STYLE_NAME, + SOLID_COLOR_CLASS, + toRichTextValue, +} from './edit'; -const toRichTextValue = ( value ) => map( value, ( ( subValue ) => subValue.children ) ); -const fromRichTextValue = ( value ) => map( value, ( subValue ) => ( { - children: subValue, -} ) ); const blockAttributes = { value: { type: 'array', @@ -31,6 +40,18 @@ const blockAttributes = { source: 'children', selector: 'cite', }, + mainColor: { + type: 'string', + }, + customMainColor: { + type: 'string', + }, + textColor: { + type: 'string', + }, + customTextColor: { + type: 'string', + }, }; export const name = 'core/pullquote'; @@ -47,52 +68,53 @@ export const settings = { attributes: blockAttributes, + styles: [ + { name: 'default', label: __( 'Regular' ), isDefault: true }, + { name: SOLID_COLOR_STYLE_NAME, label: __( 'Solid Color' ) }, + ], + supports: { - align: true, + align: [ 'left', 'right', 'wide', 'full' ], }, - edit( { attributes, setAttributes, isSelected, className } ) { - const { value, citation } = attributes; - - return ( -
-
- setAttributes( { - value: fromRichTextValue( nextValue ), - } ) - } - /* translators: the text of the quotation */ - placeholder={ __( 'Write quote…' ) } - wrapperClassName="block-library-pullquote__content" - /> - { ( ! RichText.isEmpty( citation ) || isSelected ) && ( - setAttributes( { - citation: nextCitation, - } ) - } - className="wp-block-pullquote__citation" - /> - ) } -
-
- ); - }, + edit, save( { attributes } ) { - const { value, citation } = attributes; - + const { mainColor, customMainColor, textColor, customTextColor, value, citation, className } = attributes; + const isSolidColorStyle = includes( className, SOLID_COLOR_CLASS ); + + let figureClass, figureStyles; + // Is solid color style + if ( isSolidColorStyle ) { + figureClass = getColorClassName( 'background-color', mainColor ); + if ( ! figureClass ) { + figureStyles = { + backgroundColor: customMainColor, + }; + } + // Is normal style and a custom color is being used ( we can set a style directly with its value) + } else if ( customMainColor ) { + figureStyles = { + borderColor: customMainColor, + }; + // Is normal style and a named color is being used, we need to retrieve the color value to set the style, + // as there is no expectation that themes create classes that set border colors. + } else if ( mainColor ) { + const colors = get( select( 'core/editor' ).getEditorSettings(), [ 'colors' ], [] ); + const colorObject = getColorObjectByAttributeValues( colors, mainColor ); + figureStyles = { + borderColor: colorObject.color, + }; + } + + const blockquoteTextColorClass = getColorClassName( 'color', textColor ); + const blockquoteClasses = textColor || customTextColor ? classnames( 'has-text-color', { + [ blockquoteTextColorClass ]: blockquoteTextColorClass, + } ) : undefined; + const blockquoteStyle = blockquoteTextColorClass ? undefined : { color: customTextColor }; return ( -
-
+
+
{ ! RichText.isEmpty( citation ) && }
diff --git a/packages/block-library/src/pullquote/style.scss b/packages/block-library/src/pullquote/style.scss index 7bc4b66cdc440..388e240248421 100644 --- a/packages/block-library/src/pullquote/style.scss +++ b/packages/block-library/src/pullquote/style.scss @@ -12,7 +12,7 @@ } p { - font-size: 24px; + font-size: 28px; line-height: 1.6; } @@ -20,4 +20,37 @@ footer { position: relative; } + .has-text-color a { + color: inherit; + } +} + +.wp-block-pullquote:not(.is-style-solid-color) { + background: none; +} + +.wp-block-pullquote.is-style-solid-color { + border: none; + blockquote { + margin-left: auto; + margin-right: auto; + text-align: left; + + max-width: 60%; + + p { + margin-top: 0; + margin-bottom: 0; + font-size: 32px; + } + + cite { + text-transform: none; + font-style: normal; + } + } +} + +.wp-block-pullquote cite { + color: inherit; } diff --git a/packages/block-library/src/pullquote/theme.scss b/packages/block-library/src/pullquote/theme.scss index 178d8b0fd2763..41f7ba2a45ee8 100644 --- a/packages/block-library/src/pullquote/theme.scss +++ b/packages/block-library/src/pullquote/theme.scss @@ -9,6 +9,6 @@ color: $dark-gray-600; text-transform: uppercase; font-size: $default-font-size; - font-style: italic; + font-style: normal; } } diff --git a/packages/editor/src/components/colors/utils.js b/packages/editor/src/components/colors/utils.js index 36dc77385a82b..29ab15f30e731 100644 --- a/packages/editor/src/components/colors/utils.js +++ b/packages/editor/src/components/colors/utils.js @@ -1,7 +1,8 @@ /** * External dependencies */ -import { find, kebabCase } from 'lodash'; +import { find, kebabCase, map } from 'lodash'; +import tinycolor from 'tinycolor2'; /** * Provided an array of color objects as set by the theme or by the editor defaults, @@ -56,3 +57,18 @@ export function getColorClassName( colorContextName, colorSlug ) { return `has-${ kebabCase( colorSlug ) }-${ colorContextName }`; } + +/** +* Given an array of color objects and a color value returns the color value of the most readable color in the array. +* +* @param {Array} colors Array of color objects as set by the theme or by the editor defaults. +* @param {?string} colorValue A string containing the color value. +* +* @return {string} String with the color value of the most readable color. +*/ +export function getMostReadableColor( colors, colorValue ) { + return tinycolor.mostReadable( + colorValue, + map( colors, 'color' ) + ).toHexString(); +} diff --git a/packages/editor/src/components/colors/with-colors.js b/packages/editor/src/components/colors/with-colors.js index 3bf1508872ba2..3d1bccc8c2656 100644 --- a/packages/editor/src/components/colors/with-colors.js +++ b/packages/editor/src/components/colors/with-colors.js @@ -13,7 +13,7 @@ import { compose, createHigherOrderComponent } from '@wordpress/compose'; /** * Internal dependencies */ -import { getColorClassName, getColorObjectByColorValue, getColorObjectByAttributeValues } from './utils'; +import { getColorClassName, getColorObjectByColorValue, getColorObjectByAttributeValues, getMostReadableColor } from './utils'; const DEFAULT_COLORS = []; @@ -54,10 +54,18 @@ export default ( ...args ) => { super( props ); this.setters = this.createSetters(); + this.colorUtils = { + getMostReadableColor: this.getMostReadableColor.bind( this ), + }; this.state = {}; } + getMostReadableColor( colorValue ) { + const { colors } = this.props; + return getMostReadableColor( colors, colorValue ); + } + createSetters() { return reduce( colorMap, ( settersAccumulator, colorContext, colorAttributeName ) => { const upperFirstColorAttributeName = upperFirst( colorAttributeName ); @@ -113,6 +121,7 @@ export default ( ...args ) => { colors: undefined, ...this.state, ...this.setters, + colorUtils: this.colorUtils, } } /> );