From 8df048cddeb5d4be8adc19c52f5534a9539e6398 Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Fri, 20 Nov 2020 09:24:24 +0100 Subject: [PATCH] [Mobile] - Support background colors for Group block (#25994) * Mobile - Support background/foreground colors for Group block * Mobile - Enable ColorEdit * Mobile - Add paddings to text blocks with background color * Mobile - Add clear button to palette color screen * Mobile - Global styles context utils and tests * Mobile - Palette screen - Change text and keep the same sheet when resetting a color * Mobile - Quote block - Support to inherit colors from parent block * Mobile - Block quotation and Rich text styles * Mobile - Group block - Remove extra bottom padding when a background color is set * Mobile - Color settings - Remove clear button, to be added in another PR * Mobile - Disable ColorEdit for blocks that have color support * Mobile - Group - Background color fixes * Mobile - Update changelog --- .../src/components/block-list/block.native.js | 107 ++++++++++++------ packages/block-editor/src/hooks/color.js | 13 +-- .../block-library/src/group/edit.native.js | 35 +++++- .../src/group/editor.native.scss | 8 ++ packages/block-library/src/quote/edit.js | 2 + packages/components/src/index.native.js | 1 + .../global-styles-context/index.native.js | 38 +++++++ .../test/utils.native.js | 86 ++++++++++++++ .../global-styles-context/utils.native.js | 68 +++++++++++ .../src/block-quotation/index.native.js | 27 +++-- packages/react-native-editor/CHANGELOG.md | 1 + .../rich-text/src/component/index.native.js | 7 +- 12 files changed, 334 insertions(+), 59 deletions(-) create mode 100644 packages/components/src/mobile/global-styles-context/test/utils.native.js create mode 100644 packages/components/src/mobile/global-styles-context/utils.native.js diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index e3988a3f64ab7f..27df35cf3242a3 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -7,13 +7,18 @@ import { View, Text, TouchableWithoutFeedback, Dimensions } from 'react-native'; * WordPress dependencies */ import { Component, createRef } from '@wordpress/element'; -import { GlobalStylesContext, WIDE_ALIGNMENTS } from '@wordpress/components'; +import { + GlobalStylesContext, + getMergedGlobalStyles, + WIDE_ALIGNMENTS, +} from '@wordpress/components'; import { withDispatch, withSelect } from '@wordpress/data'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { getBlockType, __experimentalGetAccessibleBlockLabel as getAccessibleBlockLabel, } from '@wordpress/blocks'; +import { __experimentalUseEditorFeature as useEditorFeature } from '@wordpress/block-editor'; /** * Internal dependencies @@ -23,6 +28,66 @@ import BlockEdit from '../block-edit'; import BlockInvalidWarning from './block-invalid-warning'; import BlockMobileToolbar from '../block-mobile-toolbar'; +function BlockForType( { + attributes, + clientId, + contentStyle, + getBlockWidth, + insertBlocksAfter, + isSelected, + mergeBlocks, + name, + onBlockFocus, + onCaretVerticalPositionChange, + onChange, + onDeleteBlock, + onReplace, + parentWidth, + wrapperProps, +} ) { + const defaultColors = useEditorFeature( 'color.palette' ) || []; + + return ( + + { ( globalStyle ) => { + const mergedStyle = getMergedGlobalStyles( + globalStyle, + wrapperProps.style, + attributes, + defaultColors + ); + + return ( + + + + + ); + } } + + ); +} + class BlockListBlock extends Component { constructor() { super( ...arguments ); @@ -65,40 +130,12 @@ class BlockListBlock extends Component { getBlockForType() { return ( - - { ( globalStyle ) => { - const mergedStyle = { - ...globalStyle, - ...this.props.wrapperProps.style, - }; - return ( - - - - - ); - } } - + ); } diff --git a/packages/block-editor/src/hooks/color.js b/packages/block-editor/src/hooks/color.js index 2f083d9af2f942..5490e045fbc056 100644 --- a/packages/block-editor/src/hooks/color.js +++ b/packages/block-editor/src/hooks/color.js @@ -34,9 +34,6 @@ export const COLOR_SUPPORT_KEY = 'color'; const EMPTY_ARRAY = []; const hasColorSupport = ( blockType ) => { - if ( Platform.OS !== 'web' ) { - return false; - } const colorSupport = getBlockSupport( blockType, COLOR_SUPPORT_KEY ); return ( colorSupport && @@ -68,20 +65,12 @@ const hasGradientSupport = ( blockType ) => { }; const hasBackgroundColorSupport = ( blockType ) => { - if ( Platform.OS !== 'web' ) { - return false; - } - const colorSupport = getBlockSupport( blockType, COLOR_SUPPORT_KEY ); return colorSupport && colorSupport.background !== false; }; const hasTextColorSupport = ( blockType ) => { - if ( Platform.OS !== 'web' ) { - return false; - } - const colorSupport = getBlockSupport( blockType, COLOR_SUPPORT_KEY ); return colorSupport && colorSupport.text !== false; @@ -226,7 +215,7 @@ export function ColorEdit( props ) { localAttributes.current = attributes; }, [ attributes ] ); - if ( ! hasColorSupport( blockName ) ) { + if ( ! hasColorSupport( blockName ) || Platform.OS !== 'web' ) { return null; } diff --git a/packages/block-library/src/group/edit.native.js b/packages/block-library/src/group/edit.native.js index 2cb3a15e6aa123..2ec026b420a632 100644 --- a/packages/block-library/src/group/edit.native.js +++ b/packages/block-library/src/group/edit.native.js @@ -8,7 +8,7 @@ import { View } from 'react-native'; */ import { withSelect } from '@wordpress/data'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; -import { InnerBlocks, withColors } from '@wordpress/block-editor'; +import { InnerBlocks } from '@wordpress/block-editor'; import { useCallback } from '@wordpress/element'; import { WIDE_ALIGNMENTS } from '@wordpress/components'; @@ -21,7 +21,9 @@ function GroupEdit( { attributes, hasInnerBlocks, isSelected, + isLastInnerBlockSelected, getStylesFromColorScheme, + mergedStyle, } ) { const { align } = attributes; const isFullWidth = align === WIDE_ALIGNMENTS.alignments.full; @@ -64,6 +66,14 @@ function GroupEdit( { ! hasInnerBlocks && isFullWidth && styles.fullWidth, + mergedStyle, + isSelected && + hasInnerBlocks && + mergedStyle?.backgroundColor && + styles.hasBackgroundAppender, + isLastInnerBlockSelected && + mergedStyle?.backgroundColor && + styles.isLastInnerBlockSelected, ] } > @@ -72,14 +82,31 @@ function GroupEdit( { } export default compose( [ - withColors( 'backgroundColor' ), withSelect( ( select, { clientId } ) => { - const { getBlock } = select( 'core/block-editor' ); + const { + getBlock, + getBlockIndex, + hasSelectedInnerBlock, + getSelectedBlockClientId, + } = select( 'core/block-editor' ); const block = getBlock( clientId ); + const hasInnerBlocks = !! ( block && block.innerBlocks.length ); + const isInnerBlockSelected = + hasInnerBlocks && hasSelectedInnerBlock( clientId, true ); + let isLastInnerBlockSelected = false; + + if ( isInnerBlockSelected ) { + const { innerBlocks } = block; + const selectedBlockClientId = getSelectedBlockClientId(); + const totalInnerBlocks = innerBlocks.length - 1; + const blockIndex = getBlockIndex( selectedBlockClientId, clientId ); + isLastInnerBlockSelected = totalInnerBlocks === blockIndex; + } return { - hasInnerBlocks: !! ( block && block.innerBlocks.length ), + hasInnerBlocks, + isLastInnerBlockSelected, }; } ), withPreferredColorScheme, diff --git a/packages/block-library/src/group/editor.native.scss b/packages/block-library/src/group/editor.native.scss index 4015bcf95bf46f..d33982b2b36bf9 100644 --- a/packages/block-library/src/group/editor.native.scss +++ b/packages/block-library/src/group/editor.native.scss @@ -36,3 +36,11 @@ margin-left: $block-edge-to-content; margin-right: $block-edge-to-content; } + +.hasBackgroundAppender { + padding-bottom: $grid-unit-30; +} + +.isLastInnerBlockSelected { + padding-bottom: 0; +} diff --git a/packages/block-library/src/quote/edit.js b/packages/block-library/src/quote/edit.js index 0647e91d1b4895..8ad2d5204f2b8c 100644 --- a/packages/block-library/src/quote/edit.js +++ b/packages/block-library/src/quote/edit.js @@ -24,12 +24,14 @@ export default function QuoteEdit( { onReplace, className, insertBlocksAfter, + mergedStyle, } ) { const { align, value, citation } = attributes; const blockProps = useBlockProps( { className: classnames( className, { [ `has-text-align-${ align }` ]: align, } ), + style: mergedStyle, } ); return ( diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index d91eea360166f9..ccfdaf84e17be1 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -99,4 +99,5 @@ export { default as GlobalStylesContext, useGlobalStyles, withGlobalStyles, + getMergedGlobalStyles, } from './mobile/global-styles-context'; diff --git a/packages/components/src/mobile/global-styles-context/index.native.js b/packages/components/src/mobile/global-styles-context/index.native.js index 2ff310d27ee3e7..5e4263ab4ad969 100644 --- a/packages/components/src/mobile/global-styles-context/index.native.js +++ b/packages/components/src/mobile/global-styles-context/index.native.js @@ -1,10 +1,48 @@ +/** + * External dependencies + */ +import { pick } from 'lodash'; + /** * WordPress dependencies */ import { createContext, useContext } from '@wordpress/element'; +/** + * Internal dependencies + */ +import { + BLOCK_STYLE_ATTRIBUTES, + getBlockPaddings, + getBlockColors, +} from './utils'; + const GlobalStylesContext = createContext( { style: {} } ); +export const getMergedGlobalStyles = ( + globalStyle, + wrapperPropsStyle, + blockAttributes, + defaultColors +) => { + const blockStyleAttributes = pick( + blockAttributes, + BLOCK_STYLE_ATTRIBUTES + ); + const mergedStyle = { + ...globalStyle, + ...wrapperPropsStyle, + }; + const blockPaddings = getBlockPaddings( + mergedStyle, + wrapperPropsStyle, + blockStyleAttributes + ); + const blockColors = getBlockColors( blockStyleAttributes, defaultColors ); + + return { ...mergedStyle, ...blockPaddings, ...blockColors }; +}; + export const useGlobalStyles = () => { const globalStyles = useContext( GlobalStylesContext ); diff --git a/packages/components/src/mobile/global-styles-context/test/utils.native.js b/packages/components/src/mobile/global-styles-context/test/utils.native.js new file mode 100644 index 00000000000000..396dee85b215f6 --- /dev/null +++ b/packages/components/src/mobile/global-styles-context/test/utils.native.js @@ -0,0 +1,86 @@ +/** + * Internal dependencies + */ +import { getBlockPaddings, getBlockColors } from '../utils'; + +const DEFAULT_COLORS = [ + { color: '#cd2653', name: 'Accent Color', slug: 'accent' }, + { color: '#000000', name: 'Primary', slug: 'primary' }, + { color: '#6d6d6d', name: 'Secondary', slug: 'secondary' }, +]; + +describe( 'getBlockPaddings', () => { + const PADDING = 12; + + it( 'returns no paddings for a block without background color', () => { + const paddings = getBlockPaddings( + { color: 'red' }, + { backgroundColor: 'red' }, + { textColor: 'primary' } + ); + expect( paddings ).toEqual( expect.objectContaining( {} ) ); + } ); + + it( 'returns paddings for a block with background color', () => { + const paddings = getBlockPaddings( + { color: 'red' }, + {}, + { backgroundColor: 'red', textColor: 'primary' } + ); + expect( paddings ).toEqual( + expect.objectContaining( { padding: PADDING } ) + ); + } ); + + it( 'returns no paddings for an inner block without background color within a parent block with background color', () => { + const paddings = getBlockPaddings( + { backgroundColor: 'blue', color: 'yellow', padding: PADDING }, + {}, + { textColor: 'primary' } + ); + + expect( paddings ).toEqual( + expect.not.objectContaining( { padding: PADDING } ) + ); + } ); +} ); + +describe( 'getBlockColors', () => { + it( 'returns the theme colors correctly', () => { + const blockColors = getBlockColors( + { backgroundColor: 'accent', textColor: 'secondary' }, + DEFAULT_COLORS + ); + expect( blockColors ).toEqual( + expect.objectContaining( { + backgroundColor: '#cd2653', + color: '#6d6d6d', + } ) + ); + } ); + + it( 'returns custom background color correctly', () => { + const blockColors = getBlockColors( + { backgroundColor: '#222222', textColor: 'accent' }, + DEFAULT_COLORS + ); + expect( blockColors ).toEqual( + expect.objectContaining( { + backgroundColor: '#222222', + color: '#cd2653', + } ) + ); + } ); + + it( 'returns custom text color correctly', () => { + const blockColors = getBlockColors( + { textColor: '#4ddddd' }, + DEFAULT_COLORS + ); + expect( blockColors ).toEqual( + expect.objectContaining( { + color: '#4ddddd', + } ) + ); + } ); +} ); diff --git a/packages/components/src/mobile/global-styles-context/utils.native.js b/packages/components/src/mobile/global-styles-context/utils.native.js new file mode 100644 index 00000000000000..c8e2bce0c91f8a --- /dev/null +++ b/packages/components/src/mobile/global-styles-context/utils.native.js @@ -0,0 +1,68 @@ +/** + * External dependencies + */ +import { find, startsWith } from 'lodash'; + +export const BLOCK_STYLE_ATTRIBUTES = [ 'textColor', 'backgroundColor' ]; + +// Mapping style properties name to native +const BLOCK_STYLE_ATTRIBUTES_MAPPING = { + textColor: 'color', +}; + +const PADDING = 12; // solid-border-space + +export function getBlockPaddings( + mergedStyle, + wrapperPropsStyle, + blockStyleAttributes +) { + const blockPaddings = {}; + + if ( + ! mergedStyle.padding && + ( wrapperPropsStyle?.backgroundColor || + blockStyleAttributes?.backgroundColor ) + ) { + blockPaddings.padding = PADDING; + return blockPaddings; + } + + // Prevent adding extra paddings to inner blocks without background colors + if ( + mergedStyle?.padding && + ! wrapperPropsStyle?.backgroundColor && + ! blockStyleAttributes?.backgroundColor + ) { + blockPaddings.padding = undefined; + } + + return blockPaddings; +} + +export function getBlockColors( blockStyleAttributes, defaultColors ) { + const blockStyles = {}; + + Object.entries( blockStyleAttributes ).forEach( ( [ key, value ] ) => { + const isCustomColor = startsWith( value, '#' ); + let styleKey = key; + + if ( BLOCK_STYLE_ATTRIBUTES_MAPPING[ styleKey ] ) { + styleKey = BLOCK_STYLE_ATTRIBUTES_MAPPING[ styleKey ]; + } + + if ( ! isCustomColor ) { + const mappedColor = find( defaultColors, { + slug: value, + } ); + + if ( mappedColor ) { + blockStyles[ styleKey ] = mappedColor.color; + } + } else { + blockStyles[ styleKey ] = value; + } + } ); + + return blockStyles; +} diff --git a/packages/primitives/src/block-quotation/index.native.js b/packages/primitives/src/block-quotation/index.native.js index 076314e3782153..8f97ed8dc43d81 100644 --- a/packages/primitives/src/block-quotation/index.native.js +++ b/packages/primitives/src/block-quotation/index.native.js @@ -13,20 +13,33 @@ import { withPreferredColorScheme } from '@wordpress/compose'; import styles from './style.scss'; export const BlockQuotation = withPreferredColorScheme( ( props ) => { - const { getStylesFromColorScheme } = props; + const { getStylesFromColorScheme, style } = props; + + const blockQuoteStyle = [ + getStylesFromColorScheme( + styles.wpBlockQuoteLight, + styles.wpBlockQuoteDark + ), + style?.color && { + borderLeftColor: style.color, + }, + ]; + const colorStyle = style?.color ? { color: style.color } : {}; - const blockQuoteStyle = getStylesFromColorScheme( - styles.wpBlockQuoteLight, - styles.wpBlockQuoteDark - ); const newChildren = Children.map( props.children, ( child ) => { if ( child && child.props.identifier === 'citation' ) { return cloneElement( child, { - style: styles.wpBlockQuoteCitation, + style: { + ...styles.wpBlockQuoteCitation, + ...colorStyle, + }, } ); } if ( child && child.props.identifier === 'value' ) { - return cloneElement( child, { tagsToEliminate: [ 'div' ] } ); + return cloneElement( child, { + tagsToEliminate: [ 'div' ], + style: colorStyle, + } ); } return child; } ); diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 23f13ce09b25a4..24caa6a16beb7c 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -13,6 +13,7 @@ For each user feature we should also add a importance categorization label to i * [***] Adding support for selecting different unit of value in Cover and Columns blocks [#26161] * [**] Button block - Add link picker to the block settingsĀ [#26206] +* [**] Support to render background/text colors in Group, Paragraph and Quote blocksĀ [#25994] * [*] Fix theme colors syncing with the editor [#26821] ## 1.41.0 diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index 8116493bb7ac25..7e59d11c094181 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -825,9 +825,14 @@ export class RichText extends Component { maxWidth && this.state.width && maxWidth - this.state.width < 10 ? maxWidth : this.state.width; + const containerStyles = style?.padding && + style?.backgroundColor && { + padding: style.padding, + backgroundColor: style.backgroundColor, + }; return ( - + { children && children( { isSelected,