From 34fa8b2a481693bd75ab3b96aafd004c5ecbfd91 Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Wed, 26 Feb 2020 08:40:10 +0100 Subject: [PATCH] RNMobile - Cover Block: First iteration (#19722) * RNMobile - Cover Block - First iteration * Remove allowed blocks * Fix content height * RNMobile - Update Heading and Paragraph blocks to use __experimentalUseColors * RNMobile - Cover block: import correct icon * RNMobile - Cover block - Default color text for InnerBlocks * Mock use select * RNMobile - Cover block - Use memo from element, don't set min height if the content has higher height * RNMobile - Cover Block - Prettier and get opacity logic improvement * Cover block: Move onCoverSelectMedia to a utils file to be shared with mobile and renaming to attributesFromMedia * RNMobile - Cover block: don't set fontSize by default since it is not supported yet * RNMobile - ImageWithFocalPoint - Move container logic to component and prettier formatting * RNMobile - Cover block: move attributesFromMedia to shared file, renamed onlyMediaLibrary to __experimental * RNMobile - Enable Cover block for testing * RNMobile - Cover block - Allow changing the image and fix blocks with solid background * RNMobile - Cover - Fallback for theme colors * RNMobile - Cover - Fix paddings for cover blocks with and without innerblocks * RNMobile - Cover - Overlay styles as a const instead of a function * RNMobile - Cover block - Remove duplicated export * RNMobile - Paragraph - Test mock import --- .../media-placeholder/index.native.js | 4 + .../components/media-upload/index.native.js | 17 +- .../src/cover/edit-media-icon.native.js | 13 + packages/block-library/src/cover/edit.js | 41 +-- .../block-library/src/cover/edit.native.js | 270 ++++++++++++++++++ packages/block-library/src/cover/shared.js | 38 +++ .../block-library/src/cover/style.native.scss | 59 ++++ .../block-library/src/heading/edit.native.js | 91 +++--- packages/block-library/src/index.native.js | 1 + .../src/paragraph/edit.native.js | 87 ++---- .../src/paragraph/test/edit.native.js | 4 + packages/components/src/index.native.js | 1 + .../image-with-focalpoint/index.native.js | 120 ++++++++ .../image-with-focalpoint/style.native.scss | 10 + .../rich-text/src/component/index.native.js | 25 +- .../rich-text/src/component/style.native.scss | 1 + 16 files changed, 639 insertions(+), 143 deletions(-) create mode 100644 packages/block-library/src/cover/edit-media-icon.native.js create mode 100644 packages/block-library/src/cover/edit.native.js create mode 100644 packages/block-library/src/cover/style.native.scss create mode 100644 packages/components/src/mobile/image-with-focalpoint/index.native.js create mode 100644 packages/components/src/mobile/image-with-focalpoint/style.native.scss diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index e2c5864fdf8341..2b3700cd200e4d 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -37,6 +37,7 @@ function MediaPlaceholder( props ) { labels = {}, icon, onSelect, + __experimentalOnlyMediaLibrary, isAppender, disableMediaButtons, getStylesFromColorScheme, @@ -141,6 +142,9 @@ function MediaPlaceholder( props ) { { return ( diff --git a/packages/block-editor/src/components/media-upload/index.native.js b/packages/block-editor/src/components/media-upload/index.native.js index a1498d4ecb9f1a..b9543141b175b1 100644 --- a/packages/block-editor/src/components/media-upload/index.native.js +++ b/packages/block-editor/src/components/media-upload/index.native.js @@ -51,6 +51,7 @@ const siteLibrarySource = { label: __( 'WordPress Media Library' ), types: [ MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO ], icon: wordpress, + mediaLibrary: true, }; const internalSources = [ @@ -94,15 +95,18 @@ export class MediaUpload extends React.Component { } getMediaOptionsItems() { - const { allowedTypes = [] } = this.props; + const { + allowedTypes = [], + __experimentalOnlyMediaLibrary, + } = this.props; return this.getAllSources() .filter( ( source ) => { - return ( - allowedTypes.filter( ( allowedType ) => - source.types.includes( allowedType ) - ).length > 0 - ); + return __experimentalOnlyMediaLibrary + ? source.mediaLibrary + : allowedTypes.filter( ( allowedType ) => + source.types.includes( allowedType ) + ).length > 0; } ) .map( ( source ) => { return { @@ -140,6 +144,7 @@ export class MediaUpload extends React.Component { const types = allowedTypes.filter( ( type ) => mediaSource.types.includes( type ) ); + requestMediaPicker( mediaSource.id, types, multiple, ( media ) => { if ( ( multiple && media ) || ( media && media.id ) ) { onSelect( media ); diff --git a/packages/block-library/src/cover/edit-media-icon.native.js b/packages/block-library/src/cover/edit-media-icon.native.js new file mode 100644 index 00000000000000..e10b209cf35c43 --- /dev/null +++ b/packages/block-library/src/cover/edit-media-icon.native.js @@ -0,0 +1,13 @@ +/** + * WordPress dependencies + */ +import { Path, Rect, SVG } from '@wordpress/components'; + +export const EditMediaIcon = ( + + + + + + +); diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index 3dbd86ac0f85d3..b9354dc6fc7363 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -41,6 +41,7 @@ import { cover as icon } from '@wordpress/icons'; * Internal dependencies */ import { + attributesFromMedia, IMAGE_BACKGROUND_TYPE, VIDEO_BACKGROUND_TYPE, COVER_MIN_HEIGHT, @@ -159,44 +160,6 @@ function ResizableCover( { ); } -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. @@ -271,7 +234,7 @@ function CoverEdit( { gradientValue, setGradient, } = __experimentalUseGradient(); - const onSelectMedia = onCoverSelectMedia( setAttributes ); + const onSelectMedia = attributesFromMedia( setAttributes ); const toggleParallax = () => { setAttributes( { diff --git a/packages/block-library/src/cover/edit.native.js b/packages/block-library/src/cover/edit.native.js new file mode 100644 index 00000000000000..fce666fb403148 --- /dev/null +++ b/packages/block-library/src/cover/edit.native.js @@ -0,0 +1,270 @@ +/** + * External dependencies + */ +import { View, TouchableWithoutFeedback } from 'react-native'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + Icon, + ImageWithFocalPoint, + PanelBody, + RangeControl, + ToolbarButton, + ToolbarGroup, +} from '@wordpress/components'; +import { + BlockControls, + InnerBlocks, + InspectorControls, + MEDIA_TYPE_IMAGE, + MediaPlaceholder, + MediaUpload, + withColors, +} from '@wordpress/block-editor'; +import { compose, withPreferredColorScheme } from '@wordpress/compose'; +import { withSelect } from '@wordpress/data'; +import { useEffect } from '@wordpress/element'; +import { cover as icon } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; +import { + attributesFromMedia, + COVER_MIN_HEIGHT, + IMAGE_BACKGROUND_TYPE, + VIDEO_BACKGROUND_TYPE, +} from './shared'; +import { EditMediaIcon } from './edit-media-icon'; + +/** + * Constants + */ +const ALLOWED_MEDIA_TYPES = [ MEDIA_TYPE_IMAGE ]; +const INNER_BLOCKS_TEMPLATE = [ + [ + 'core/paragraph', + { + align: 'center', + placeholder: __( 'Write title…' ), + }, + ], +]; +const COVER_MAX_HEIGHT = 1000; +const COVER_DEFAULT_HEIGHT = 300; + +const Cover = ( { + attributes, + getStylesFromColorScheme, + hasChildren, + isParentSelected, + onFocus, + overlayColor, + setAttributes, +} ) => { + const { + backgroundType, + dimRatio, + focalPoint, + gradientValue, + minHeight, + url, + } = attributes; + const CONTAINER_HEIGHT = minHeight || COVER_DEFAULT_HEIGHT; + + const hasBackground = !! ( + url || + attributes.overlayColor || + overlayColor.color || + gradientValue + ); + + // 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. + useEffect( () => { + // While we don't support theme colors + if ( ! attributes.overlayColor || ( ! attributes.overlay && url ) ) { + setAttributes( { childrenStyles: styles.defaultColor } ); + } + }, [ setAttributes ] ); + + const onSelectMedia = ( media ) => { + const onSelect = attributesFromMedia( setAttributes ); + onSelect( media ); + }; + + const onHeightChange = ( value ) => { + if ( minHeight || value !== COVER_DEFAULT_HEIGHT ) { + setAttributes( { minHeight: value } ); + } + }; + + const onOpactiyChange = ( value ) => { + setAttributes( { dimRatio: value } ); + }; + + const overlayStyles = [ + styles.overlay, + { + backgroundColor: + overlayColor && overlayColor.color + ? overlayColor.color + : styles.overlay.color, + // Set opacity to 1 while video / theme color support is not available + opacity: + url && VIDEO_BACKGROUND_TYPE !== backgroundType + ? dimRatio / 100 + : 1, + }, + // While we don't support theme colors we add a default bg color + ! overlayColor.color && ! url + ? getStylesFromColorScheme( + styles.backgroundSolid, + styles.backgroundSolidDark + ) + : {}, + ]; + + const placeholderIconStyle = getStylesFromColorScheme( + styles.icon, + styles.iconDark + ); + + const placeholderIcon = ; + + const toolbarControls = ( open ) => ( + + + + + + ); + + const controls = ( + + + + + { url ? ( + + + + ) : null } + + ); + + const containerStyles = [ + hasChildren && ! isParentSelected && styles.regularMediaPadding, + hasChildren && isParentSelected && styles.innerPadding, + ]; + + const background = ( openMediaOptions, getMediaOptions ) => ( + + + { getMediaOptions() } + { isParentSelected && toolbarControls( openMediaOptions ) } + + { IMAGE_BACKGROUND_TYPE === backgroundType && ( + + ) } + + + ); + + if ( ! hasBackground ) { + return ( + + + + ); + } + + return ( + + { controls } + + + + + + + + { + return background( open, getMediaOptions ); + } } + /> + + + ); +}; + +export default compose( [ + withColors( { overlayColor: 'background-color' } ), + withSelect( ( select, { clientId } ) => { + const { getSelectedBlockClientId, getBlockCount } = select( + 'core/block-editor' + ); + + const selectedBlockClientId = getSelectedBlockClientId(); + const hasChildren = getBlockCount( clientId ); + + return { + hasChildren, + isParentSelected: selectedBlockClientId === clientId, + }; + } ), + withPreferredColorScheme, +] )( Cover ); diff --git a/packages/block-library/src/cover/shared.js b/packages/block-library/src/cover/shared.js index 34f5bd8e257f60..bf1e794606aa3d 100644 --- a/packages/block-library/src/cover/shared.js +++ b/packages/block-library/src/cover/shared.js @@ -10,3 +10,41 @@ export function dimRatioToClass( ratio ) { ? null : 'has-background-dim-' + 10 * Math.round( ratio / 10 ); } + +export function attributesFromMedia( 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-library/src/cover/style.native.scss b/packages/block-library/src/cover/style.native.scss new file mode 100644 index 00000000000000..2141f715a2b7e5 --- /dev/null +++ b/packages/block-library/src/cover/style.native.scss @@ -0,0 +1,59 @@ +.icon { + fill: $gray-dark; + height: 24px; + width: 24px; +} + +.iconDark { + fill: $white; +} + +.innerPadding { + padding: $block-selected-to-content; +} + +.regularMediaPadding { + padding: $block-edge-to-content; +} + +.denseMediaPadding { + padding: $block-media-container-to-content; +} + +.backgroundContainer { + overflow: hidden; + width: 100%; + position: relative; +} + +.content { + justify-content: center; + width: 100%; + z-index: 3; +} + +.overlay { + width: 100%; + height: 100%; + z-index: 2; + color: $black; + position: absolute; +} + +.defaultColor { + color: $white; +} + +.background { + width: 100%; + height: 100%; + position: absolute; +} + +.backgroundSolid { + background-color: $gray-lighten-30; +} + +.backgroundSolidDark { + background-color: $background-dark-secondary; +} diff --git a/packages/block-library/src/heading/edit.native.js b/packages/block-library/src/heading/edit.native.js index fdf8480713bbf2..8d8d8b49aef99a 100644 --- a/packages/block-library/src/heading/edit.native.js +++ b/packages/block-library/src/heading/edit.native.js @@ -12,7 +12,11 @@ import { View } from 'react-native'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { RichText, BlockControls } from '@wordpress/block-editor'; +import { + RichText, + BlockControls, + __experimentalUseColors, +} from '@wordpress/block-editor'; import { createBlock } from '@wordpress/blocks'; const HeadingEdit = ( { @@ -22,41 +26,56 @@ const HeadingEdit = ( { onReplace, setAttributes, style, -} ) => ( - - - - setAttributes( { level: newLevel } ) - } - isCollapsed={ false } - /> - - setAttributes( { content: value } ) } - onMerge={ mergeBlocks } - onSplit={ ( value ) => { - if ( ! value ) { - return createBlock( 'core/paragraph' ); - } +} ) => { + const { align, content, level, placeholder } = attributes; - return createBlock( 'core/heading', { - ...attributes, - content: value, - } ); - } } - onReplace={ onReplace } - onRemove={ () => onReplace( [] ) } - placeholder={ attributes.placeholder || __( 'Write heading…' ) } - /> - -); + /* eslint-disable @wordpress/no-unused-vars-before-return */ + const { TextColor } = __experimentalUseColors( [ + { name: 'textColor', property: 'color' }, + ] ); + /* eslint-enable @wordpress/no-unused-vars-before-return */ + + return ( + + + + setAttributes( { level: newLevel } ) + } + isCollapsed={ false } + /> + + + + setAttributes( { content: value } ) + } + onMerge={ mergeBlocks } + onSplit={ ( value ) => { + if ( ! value ) { + return createBlock( 'core/paragraph' ); + } + + return createBlock( 'core/heading', { + ...attributes, + content: value, + } ); + } } + onReplace={ onReplace } + onRemove={ () => onReplace( [] ) } + placeholder={ placeholder || __( 'Write heading…' ) } + textAlign={ align } + /> + + + ); +}; export default HeadingEdit; diff --git a/packages/block-library/src/index.native.js b/packages/block-library/src/index.native.js index 9cd943dccb7263..262b1bf7354c19 100644 --- a/packages/block-library/src/index.native.js +++ b/packages/block-library/src/index.native.js @@ -153,6 +153,7 @@ export const registerCoreBlocks = () => { spacer, shortcode, devOnly( verse ), + cover, ].forEach( registerBlock ); setDefaultBlockName( paragraph.name ); diff --git a/packages/block-library/src/paragraph/edit.native.js b/packages/block-library/src/paragraph/edit.native.js index ae9bfe819131ef..b02cdc3662125a 100644 --- a/packages/block-library/src/paragraph/edit.native.js +++ b/packages/block-library/src/paragraph/edit.native.js @@ -1,71 +1,44 @@ -/** - * External dependencies - */ -import { View } from 'react-native'; - /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { Component } from '@wordpress/element'; import { createBlock } from '@wordpress/blocks'; import { AlignmentToolbar, BlockControls, RichText, + __experimentalUseColors, } from '@wordpress/block-editor'; -/** - * Internal dependencies - */ - const name = 'core/paragraph'; -class ParagraphEdit extends Component { - constructor( props ) { - super( props ); - this.onReplace = this.onReplace.bind( this ); - } - - onReplace( blocks ) { - const { attributes, onReplace } = this.props; - onReplace( - blocks.map( ( block, index ) => - index === 0 && block.name === name - ? { - ...block, - attributes: { - ...attributes, - ...block.attributes, - }, - } - : block - ) - ); - } - - render() { - const { - attributes, - setAttributes, - mergeBlocks, - onReplace, - style, - } = this.props; +function ParagraphBlock( { + attributes, + mergeBlocks, + onReplace, + setAttributes, + style, +} ) { + const { align, content, placeholder } = attributes; - const { align, content, placeholder } = attributes; + /* eslint-disable @wordpress/no-unused-vars-before-return */ + const { TextColor } = __experimentalUseColors( [ + { name: 'textColor', property: 'color' }, + ] ); + /* eslint-enable @wordpress/no-unused-vars-before-return */ - return ( - - - { - setAttributes( { align: nextAlign } ); - } } - /> - + return ( + <> + + { + setAttributes( { align: nextAlign } ); + } } + /> + + - - ); - } + + + ); } -export default ParagraphEdit; +export default ParagraphBlock; diff --git a/packages/block-library/src/paragraph/test/edit.native.js b/packages/block-library/src/paragraph/test/edit.native.js index 3a7fea4e28e35d..1602f2f4fa7069 100644 --- a/packages/block-library/src/paragraph/test/edit.native.js +++ b/packages/block-library/src/paragraph/test/edit.native.js @@ -12,6 +12,10 @@ import Paragraph from '../edit'; * WordPress dependencies */ jest.mock( '@wordpress/blocks' ); +jest.mock( '../../../../data/src/components/use-select', () => () => ( { + attributes: () => {}, + settingsColors: [], +} ) ); const getTestComponentWithContent = ( content ) => { return shallow( diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index cff040b1a732f5..067470d0a34572 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -55,3 +55,4 @@ export { default as Picker } from './mobile/picker'; export { default as ReadableContentView } from './mobile/readable-content-view'; export { default as StepperControl } from './mobile/stepper-control'; export { default as CycleSelectControl } from './mobile/cycle-select-control'; +export { default as ImageWithFocalPoint } from './mobile/image-with-focalpoint'; diff --git a/packages/components/src/mobile/image-with-focalpoint/index.native.js b/packages/components/src/mobile/image-with-focalpoint/index.native.js new file mode 100644 index 00000000000000..0e542eaad82770 --- /dev/null +++ b/packages/components/src/mobile/image-with-focalpoint/index.native.js @@ -0,0 +1,120 @@ +/** + * External dependencies + */ +import { Image, View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { useState, useEffect, memo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; + +const ImageWithFocalPoint = ( { focalPoint, url } ) => { + const [ originalImageData, setOriginalImageData ] = useState( null ); + const [ containerSize, setContainerSize ] = useState( null ); + + useEffect( () => { + if ( url ) { + Image.getSize( url, ( width, height ) => { + setOriginalImageData( { + width, + height, + aspectRatio: width / height, + } ); + } ); + } + }, [] ); + + const onContainerLayout = ( event ) => { + const { height, width } = event.nativeEvent.layout; + setContainerSize( { width, height } ); + }; + + const getFocalPointOffset = ( + imageRatio, + container, + imageSize, + focusPoint + ) => { + const containerCenter = Math.floor( container / 2 ); + const scaledImage = Math.floor( imageSize / imageRatio ); + const focus = Math.floor( focusPoint * scaledImage ); + let focusOffset = focus - containerCenter; + const offsetRest = scaledImage - focus; + const containerRest = container - containerCenter; + + if ( offsetRest < containerRest ) { + focusOffset -= containerRest - offsetRest; + } + + if ( focusOffset < 0 ) { + focusOffset = 0; + } + + return -focusOffset; + }; + + const getImageStyles = () => { + const imageStyle = {}; + if ( focalPoint && containerSize && originalImageData ) { + let horizontalOffset = 0; + let verticalOffset = 0; + const widthRatio = originalImageData.width / containerSize.width; + const heightRatio = originalImageData.height / containerSize.height; + + imageStyle.resizeMode = 'stretch'; + + if ( widthRatio > heightRatio ) { + horizontalOffset = getFocalPointOffset( + heightRatio, + containerSize.width, + originalImageData.width, + focalPoint.x + ); + imageStyle.width = undefined; + imageStyle.left = horizontalOffset; + } else if ( widthRatio < heightRatio ) { + verticalOffset = getFocalPointOffset( + widthRatio, + containerSize.height, + originalImageData.height, + focalPoint.y + ); + imageStyle.height = undefined; + imageStyle.top = verticalOffset; + } + + return imageStyle; + } + + return imageStyle; + }; + + return ( + + + + ); +}; + +export default memo( ImageWithFocalPoint ); diff --git a/packages/components/src/mobile/image-with-focalpoint/style.native.scss b/packages/components/src/mobile/image-with-focalpoint/style.native.scss new file mode 100644 index 00000000000000..140d8f514569bd --- /dev/null +++ b/packages/components/src/mobile/image-with-focalpoint/style.native.scss @@ -0,0 +1,10 @@ +.container { + height: 100%; + position: absolute; + width: 100%; +} + +.image { + position: absolute; + width: 100%; +} diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index 80c2471511fbf7..22c9a3d3d050f1 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -5,7 +5,7 @@ */ import RCTAztecView from 'react-native-aztec'; import { View, Platform } from 'react-native'; -import { pickBy } from 'lodash'; +import { get, pickBy } from 'lodash'; import memize from 'memize'; /** @@ -698,6 +698,7 @@ export class RichText extends Component { minWidth, maxWidth, formatTypes, + parentBlockStyles, withoutInteractiveFormatting, } = this.props; @@ -806,6 +807,7 @@ export class RichText extends Component { } } } style={ { + backgroundColor: styles.richText.backgroundColor, ...style, ...( this.isIOS && minWidth && maxWidth ? { width } @@ -836,7 +838,11 @@ export class RichText extends Component { } onSelectionChange={ this.onSelectionChangeFromAztec } blockType={ { tag: tagName } } - color={ ( style && style.color ) || defaultColor } + color={ + ( style && style.color ) || + ( parentBlockStyles && parentBlockStyles.color ) || + defaultColor + } linkTextColor={ defaultTextDecorationColor } maxImagesWidth={ 200 } fontFamily={ this.props.fontFamily || defaultFontFamily } @@ -876,8 +882,17 @@ RichText.defaultProps = { }; export default compose( [ - withSelect( ( select ) => ( { - formatTypes: select( 'core/rich-text' ).getFormatTypes(), - } ) ), + withSelect( ( select, { clientId } ) => { + const { getBlockParents, getBlock } = select( 'core/block-editor' ); + const parents = getBlockParents( clientId, true ); + const parentBlock = parents ? getBlock( parents[ 0 ] ) : undefined; + const parentBlockStyles = + get( parentBlock, [ 'attributes', 'childrenStyles' ] ) || {}; + + return { + formatTypes: select( 'core/rich-text' ).getFormatTypes(), + ...{ parentBlockStyles }, + }; + } ), withPreferredColorScheme, ] )( RichText ); diff --git a/packages/rich-text/src/component/style.native.scss b/packages/rich-text/src/component/style.native.scss index fe957504135651..7a885c83063bba 100644 --- a/packages/rich-text/src/component/style.native.scss +++ b/packages/rich-text/src/component/style.native.scss @@ -3,6 +3,7 @@ font-family: $default-regular-font; color: $gray-900; text-decoration-color: $blue-500; + background-color: transparent; } .richTextDark {