From 8ba0a42e7d461722129c2c21b77b2625a1f8a255 Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Fri, 4 Sep 2020 12:44:47 +0200 Subject: [PATCH] [Mobile] Add support for full-width/wide alignment options (#24598) * Mobile - Full / Wide alignment support * Mobile - Full / Width alignment support: wide setting improvements * Mobile - Full / Wide alignment support - Custom align native hook to filter unsupported blocks * Mobile - Full / Wide alignment support - margin improvements and fixes * Mobile - BlockListItem - Remove unused selector * Mobile - Full / Wide alignments - props order * Mobile - Full / wide alignment - use declared const * Mobile - Full / Wide alignment - Remove right / left support * Mobile - Full width / wide alignment - Add Imade block support and Group appender fix * Mobile - Full width / wide alignment - Cover and group improvements * Mobile - Full width / wide alignments - Improve margins * Mobile - Full width / wide alignment options - Fix appender and selected border for full width blocks * Mobile - Full width / wide alignment - Update margins * Mobile - Wide / full width alignments - Margin fixes and improvements * Mobile - Full width / wide alignment fix margins and max width * Mobile - Remove unused style * Mobile - Full-width / wide alignment options - Restore cover paddings for inner blocks * Mobile - Full width / wide alignments update breakpoints * Mobile - Full-width / Wide alignments - Improve max width and margins for smaller screens * Mobile - Full-width / Wide alignments - Remove unused variable * Mobile - Full-width / Wide alignments - New utils with alignments and breakpoints * Mobile - Full-width fix for Android devices window width * React Native Editor - Update changelog --- .../block-list/block-list-item.native.js | 96 ++++++++++++++++++- .../block-list/block-list-item.native.scss | 12 +++ .../src/components/block-list/block.native.js | 12 ++- .../components/block-list/block.native.scss | 9 ++ .../src/components/block-list/index.native.js | 9 +- .../block-mobile-toolbar/index.native.js | 5 +- .../block-mobile-toolbar/style.native.scss | 5 + .../block-editor/src/hooks/align.native.js | 51 ++++++++++ .../block-editor/src/hooks/index.native.js | 4 +- .../block-editor/src/store/defaults.native.js | 14 +++ .../block-library/src/cover/edit.native.js | 1 + .../block-library/src/cover/style.native.scss | 4 + .../block-library/src/group/edit.native.js | 40 +++++++- .../src/group/editor.native.scss | 10 ++ .../block-library/src/image/edit.native.js | 24 ++++- packages/components/src/index.native.js | 4 + .../src/mobile/image/index.native.js | 5 +- .../readable-content-view/index.native.js | 79 ++++++++++++--- .../readable-content-view/style.native.scss | 16 ++++ .../src/mobile/utils/alignments.native.js | 14 +++ packages/react-native-editor/CHANGELOG.md | 1 + 21 files changed, 383 insertions(+), 32 deletions(-) create mode 100644 packages/block-editor/src/components/block-list/block-list-item.native.scss create mode 100644 packages/block-editor/src/hooks/align.native.js create mode 100644 packages/block-editor/src/store/defaults.native.js create mode 100644 packages/components/src/mobile/utils/alignments.native.js diff --git a/packages/block-editor/src/components/block-list/block-list-item.native.js b/packages/block-editor/src/components/block-list/block-list-item.native.js index 3145a4038bab6..8623514b1b44c 100644 --- a/packages/block-editor/src/components/block-list/block-list-item.native.js +++ b/packages/block-editor/src/components/block-list/block-list-item.native.js @@ -9,21 +9,89 @@ import { View } from 'react-native'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { ReadableContentView } from '@wordpress/components'; +import { + ReadableContentView, + WIDE_ALIGNMENTS, + ALIGNMENT_BREAKPOINTS, +} from '@wordpress/components'; /** * Internal dependencies */ import BlockListBlock from './block'; import BlockInsertionPoint from './insertion-point'; +import styles from './block-list-item.native.scss'; const stretchStyle = { flex: 1, }; export class BlockListItem extends Component { + constructor() { + super( ...arguments ); + + this.onLayout = this.onLayout.bind( this ); + + this.state = { + blockWidth: 0, + }; + } + + onLayout( { nativeEvent } ) { + const { layout } = nativeEvent; + const { blockWidth } = this.state; + + if ( blockWidth !== layout.width ) { + this.setState( { blockWidth: layout.width } ); + } + } + + getMarginHorizontal() { + const { + blockAlignment, + marginHorizontal, + parentBlockAlignment, + } = this.props; + const { blockWidth } = this.state; + + if ( blockAlignment === WIDE_ALIGNMENTS.alignments.full ) { + return 0; + } + + if ( blockAlignment === WIDE_ALIGNMENTS.alignments.wide ) { + return marginHorizontal; + } + + if ( + parentBlockAlignment === WIDE_ALIGNMENTS.alignments.full && + blockWidth <= ALIGNMENT_BREAKPOINTS.medium + ) { + return marginHorizontal * 2; + } + + return marginHorizontal; + } + + getContentStyles( readableContentViewStyle ) { + const { blockAlignment, hasParents } = this.props; + const isFullWidth = blockAlignment === WIDE_ALIGNMENTS.alignments.full; + + return [ + readableContentViewStyle, + isFullWidth && + ! hasParents && { + width: styles.fullAlignment.width, + }, + isFullWidth && + hasParents && { + paddingHorizontal: styles.fullAlignmentPadding.paddingLeft, + }, + ]; + } + render() { const { + blockAlignment, clientId, isReadOnly, shouldShowInsertionPointBefore, @@ -34,11 +102,16 @@ export class BlockListItem extends Component { } = this.props; const readableContentViewStyle = contentResizeMode === 'stretch' && stretchStyle; + return ( - + { shouldShowInsertionPointBefore && ( @@ -48,6 +121,7 @@ export class BlockListItem extends Component { showTitle={ false } clientId={ clientId } { ...restProps } + marginHorizontal={ this.getMarginHorizontal() } /> { ! shouldShowInnerBlockAppender() && shouldShowInsertionPointAfter && ( @@ -67,6 +141,8 @@ export default compose( [ getBlockInsertionPoint, isBlockInsertionPointVisible, getSettings, + getBlockParents, + __unstableGetBlockWithoutInnerBlocks, } = select( 'core/block-editor' ); const blockClientIds = getBlockOrder( rootClientId ); @@ -92,10 +168,24 @@ export default compose( [ const isReadOnly = getSettings().readOnly; + const block = __unstableGetBlockWithoutInnerBlocks( clientId ); + const { attributes } = block || {}; + const { align } = attributes || {}; + const parents = getBlockParents( clientId, true ); + const hasParents = !! parents.length; + const parentBlock = hasParents + ? __unstableGetBlockWithoutInnerBlocks( parents[ 0 ] ) + : {}; + const { align: parentBlockAlignment } = + parentBlock?.attributes || {}; + return { shouldShowInsertionPointBefore, shouldShowInsertionPointAfter, isReadOnly, + hasParents, + blockAlignment: align, + parentBlockAlignment, }; } ), diff --git a/packages/block-editor/src/components/block-list/block-list-item.native.scss b/packages/block-editor/src/components/block-list/block-list-item.native.scss new file mode 100644 index 0000000000000..8e2eca6143034 --- /dev/null +++ b/packages/block-editor/src/components/block-list/block-list-item.native.scss @@ -0,0 +1,12 @@ +.stretch { + flex: 1; +} + +.fullAlignment { + margin-left: 0; + width: 100%; +} + +.fullAlignmentPadding { + padding: $block-edge-to-content; +} 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 ada2750b33d02..78e0c546bfcc2 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -1,13 +1,13 @@ /** * External dependencies */ -import { View, Text, TouchableWithoutFeedback } from 'react-native'; +import { View, Text, TouchableWithoutFeedback, Dimensions } from 'react-native'; /** * WordPress dependencies */ import { Component, createRef } from '@wordpress/element'; -import { GlobalStylesContext } from '@wordpress/components'; +import { GlobalStylesContext, WIDE_ALIGNMENTS } from '@wordpress/components'; import { withDispatch, withSelect } from '@wordpress/data'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { @@ -136,7 +136,7 @@ class BlockListBlock extends Component { } const { blockWidth } = this.state; - + const { align } = attributes; const accessibilityLabel = getAccessibleBlockLabel( blockType, attributes, @@ -144,6 +144,8 @@ class BlockListBlock extends Component { ); const accessible = ! ( isSelected || isInnerBlockSelected ); + const isFullWidth = align === WIDE_ALIGNMENTS.alignments.full; + const screenWidth = Math.floor( Dimensions.get( 'window' ).width ); return ( ) } diff --git a/packages/block-editor/src/components/block-list/block.native.scss b/packages/block-editor/src/components/block-list/block.native.scss index aa3d6d1248f59..fd5b60e15df2f 100644 --- a/packages/block-editor/src/components/block-list/block.native.scss +++ b/packages/block-editor/src/components/block-list/block.native.scss @@ -18,6 +18,11 @@ border-color: $gray-70; } +.borderFullWidth { + left: 0; + right: 0; +} + .dimmed { opacity: $dimmed-opacity; } @@ -67,3 +72,7 @@ border-radius: 2px; border-style: dashed; } + +.fullWidthPadding { + padding: $block-selected-to-content; +} diff --git a/packages/block-editor/src/components/block-list/index.native.js b/packages/block-editor/src/components/block-list/index.native.js index 1ea9a0ed371c5..4b527db87251d 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -14,6 +14,7 @@ import { createBlock } from '@wordpress/blocks'; import { KeyboardAwareFlatList, ReadableContentView, + WIDE_ALIGNMENTS, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; @@ -387,7 +388,13 @@ class EmptyListComponent extends Component { return ( - + { const [ fillsLength, setFillsLength ] = useState( null ); const wrapBlockSettings = blockWidth < BREAKPOINTS.wrapSettings; const wrapBlockMover = blockWidth <= BREAKPOINTS.wrapMover; return ( - + { ! wrapBlockMover && ( { + if ( + ! WIDE_ALIGNMENTS.supportedBlocks.includes( name ) && + hasBlockSupport( settings, 'align' ) + ) { + const blockAlign = settings.supports.align; + + settings.supports = { + ...settings.supports, + align: Array.isArray( blockAlign ) + ? without( + blockAlign, + ...Object.values( WIDE_ALIGNMENTS.alignments ) + ) + : blockAlign, + alignWide: false, + }; + settings.attributes = { + ...settings.attributes, + align: { + type: 'string', + // Allow for '' since it is used by updateAlignment function + // in withToolbarControls for special cases with defined default values. + enum: [ ...ALIGNMENTS, '' ], + }, + }; + } + + return settings; + } +); diff --git a/packages/block-editor/src/hooks/index.native.js b/packages/block-editor/src/hooks/index.native.js index 4896bdda6e6a1..5d9b2f5393a8d 100644 --- a/packages/block-editor/src/hooks/index.native.js +++ b/packages/block-editor/src/hooks/index.native.js @@ -1,10 +1,12 @@ /** * Internal dependencies */ -export { AlignmentHookSettingsProvider } from './align'; +import { AlignmentHookSettingsProvider } from './align'; import './anchor'; import './custom-class-name'; import './generated-class-name'; import './style'; import './color'; import './font-size'; + +export { AlignmentHookSettingsProvider }; diff --git a/packages/block-editor/src/store/defaults.native.js b/packages/block-editor/src/store/defaults.native.js new file mode 100644 index 0000000000000..125ee6c85c1f4 --- /dev/null +++ b/packages/block-editor/src/store/defaults.native.js @@ -0,0 +1,14 @@ +/** + * Internal dependencies + */ +import { + PREFERENCES_DEFAULTS, + SETTINGS_DEFAULTS as SETTINGS, +} from './defaults.js'; + +const SETTINGS_DEFAULTS = { + ...SETTINGS, + alignWide: true, +}; + +export { PREFERENCES_DEFAULTS, SETTINGS_DEFAULTS }; diff --git a/packages/block-library/src/cover/edit.native.js b/packages/block-library/src/cover/edit.native.js index 39e6d685b0557..5485cbf004a54 100644 --- a/packages/block-library/src/cover/edit.native.js +++ b/packages/block-library/src/cover/edit.native.js @@ -410,6 +410,7 @@ const Cover = ( { onSelectMediaUploadOption={ onSelectMedia } openMediaOptions={ openMediaOptionsRef.current } url={ url } + width={ styles.image.width } /> ) } diff --git a/packages/block-library/src/cover/style.native.scss b/packages/block-library/src/cover/style.native.scss index 73a8f82c2415c..33fdadffd6822 100644 --- a/packages/block-library/src/cover/style.native.scss +++ b/packages/block-library/src/cover/style.native.scss @@ -136,6 +136,10 @@ z-index: 4; } +.image { + width: 100%; +} + .colorPaletteWrapper { min-height: 50px; } diff --git a/packages/block-library/src/group/edit.native.js b/packages/block-library/src/group/edit.native.js index 257ba8b4e628c..2cb3a15e6aa12 100644 --- a/packages/block-library/src/group/edit.native.js +++ b/packages/block-library/src/group/edit.native.js @@ -9,12 +9,36 @@ import { View } from 'react-native'; import { withSelect } from '@wordpress/data'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { InnerBlocks, withColors } from '@wordpress/block-editor'; +import { useCallback } from '@wordpress/element'; +import { WIDE_ALIGNMENTS } from '@wordpress/components'; + /** * Internal dependencies */ import styles from './editor.scss'; -function GroupEdit( { hasInnerBlocks, isSelected, getStylesFromColorScheme } ) { +function GroupEdit( { + attributes, + hasInnerBlocks, + isSelected, + getStylesFromColorScheme, +} ) { + const { align } = attributes; + const isFullWidth = align === WIDE_ALIGNMENTS.alignments.full; + + const renderAppender = useCallback( + () => ( + + + + ), + [ align, hasInnerBlocks ] + ); + if ( ! isSelected && ! hasInnerBlocks ) { return ( - + + ); } diff --git a/packages/block-library/src/group/editor.native.scss b/packages/block-library/src/group/editor.native.scss index 7f8a9ce132246..4015bcf95bf46 100644 --- a/packages/block-library/src/group/editor.native.scss +++ b/packages/block-library/src/group/editor.native.scss @@ -26,3 +26,13 @@ .innerBlocks { margin-bottom: $block-edge-to-content; } + +.fullWidth { + margin-left: $block-edge-to-content; + margin-right: $block-edge-to-content; +} + +.fullWidthAppender { + margin-left: $block-edge-to-content; + margin-right: $block-edge-to-content; +} diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 6c04fe28b64fc..d0a22952bda3c 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -24,6 +24,7 @@ import { ToolbarButton, ToolbarGroup, Image, + WIDE_ALIGNMENTS, } from '@wordpress/components'; import { BlockCaption, @@ -95,7 +96,6 @@ export class ImageEdit extends React.Component { componentDidMount() { const { attributes, setAttributes } = this.props; - // This will warn when we have `id` defined, while `url` is undefined. // This may help track this issue: https://github.com/wordpress-mobile/WordPress-Android/issues/9768 // where a cancelled image upload was resulting in a subsequent crash. @@ -235,7 +235,15 @@ export class ImageEdit extends React.Component { } updateAlignment( nextAlign ) { - this.props.setAttributes( { align: nextAlign } ); + const extraUpdatedAttributes = Object.values( + WIDE_ALIGNMENTS.alignments + ).includes( nextAlign ) + ? { width: undefined, height: undefined } + : {}; + this.props.setAttributes( { + ...extraUpdatedAttributes, + align: nextAlign, + } ); } onSetLinkDestination( href ) { @@ -320,6 +328,15 @@ export class ImageEdit extends React.Component { ); } + getWidth() { + const { attributes } = this.props; + const { align, width } = attributes; + + return Object.values( WIDE_ALIGNMENTS.alignments ).includes( align ) + ? '100%' + : width; + } + render() { const { isCaptionSelected } = this.state; const { @@ -332,7 +349,6 @@ export class ImageEdit extends React.Component { const { align, url, - width, alt, href, id, @@ -482,8 +498,8 @@ export class ImageEdit extends React.Component { openMediaOptions={ openMediaOptions } retryMessage={ retryMessage } url={ url } - width={ width } shapeStyle={ styles[ className ] } + width={ this.getWidth() } /> ); } } diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 18c46804e64ef..45ef4d299c098 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -79,6 +79,10 @@ export { default as ImageEditingButton } from './mobile/image/image-editing-butt // Utils export { colorsUtils } from './mobile/color-settings/utils'; +export { + WIDE_ALIGNMENTS, + ALIGNMENT_BREAKPOINTS, +} from './mobile/utils/alignments'; export { default as GlobalStylesContext, diff --git a/packages/components/src/mobile/image/index.native.js b/packages/components/src/mobile/image/index.native.js index fae28f8967036..1869150135e1a 100644 --- a/packages/components/src/mobile/image/index.native.js +++ b/packages/components/src/mobile/image/index.native.js @@ -117,7 +117,10 @@ const ImageComponent = ( { styles.imageContent, { width: - imageData && imageWidth > 0 && imageWidth < containerSize?.width + imageWidth === styles.wide.width || + ( imageData && + imageWidth > 0 && + imageWidth < containerSize?.width ) ? imageWidth : customWidth, }, diff --git a/packages/components/src/mobile/readable-content-view/index.native.js b/packages/components/src/mobile/readable-content-view/index.native.js index ec1f67a4d98f8..595d7333e91d5 100644 --- a/packages/components/src/mobile/readable-content-view/index.native.js +++ b/packages/components/src/mobile/readable-content-view/index.native.js @@ -3,25 +3,78 @@ */ import { View, Dimensions } from 'react-native'; +/** + * WordPress dependencies + */ +import { useState, useEffect } from '@wordpress/element'; +import { ALIGNMENT_BREAKPOINTS, WIDE_ALIGNMENTS } from '@wordpress/components'; /** * Internal dependencies */ import styles from './style.scss'; -const ReadableContentView = ( { reversed, children, style } ) => ( - - - { children } +const PIXEL_RATIO = 2; + +const ReadableContentView = ( { align, reversed, children, style } ) => { + const { width, height } = Dimensions.get( 'window' ); + const [ windowWidth, setWindowWidth ] = useState( width ); + const [ windowRatio, setWindowRatio ] = useState( width / height ); + + function onDimensionsChange( { window } ) { + setWindowWidth( window.width ); + setWindowRatio( window.width / window.height ); + } + + useEffect( () => { + if ( align === WIDE_ALIGNMENTS.alignments.wide ) { + Dimensions.addEventListener( 'change', onDimensionsChange ); + } + + return () => { + if ( align === WIDE_ALIGNMENTS.alignments.wide ) { + Dimensions.removeEventListener( 'change', onDimensionsChange ); + } + }; + }, [ align ] ); + + function getWideStyles() { + if ( + windowRatio >= PIXEL_RATIO && + windowWidth < ALIGNMENT_BREAKPOINTS.large + ) { + return styles.wideLandscape; + } + + if ( windowWidth <= ALIGNMENT_BREAKPOINTS.small ) { + return { maxWidth: windowWidth }; + } + + if ( + windowWidth >= ALIGNMENT_BREAKPOINTS.medium && + windowWidth < ALIGNMENT_BREAKPOINTS.wide + ) { + return styles.wideMedium; + } + } + + return ( + + + { children } + - -); + ); +}; const isContentMaxWidth = () => { const { width } = Dimensions.get( 'window' ); diff --git a/packages/components/src/mobile/readable-content-view/style.native.scss b/packages/components/src/mobile/readable-content-view/style.native.scss index 61097fc4a2bbd..2560d7ac6b3e1 100644 --- a/packages/components/src/mobile/readable-content-view/style.native.scss +++ b/packages/components/src/mobile/readable-content-view/style.native.scss @@ -12,3 +12,19 @@ width: 100%; max-width: 580; } + +.full { + max-width: 100%; +} + +.wide { + max-width: 1054; +} + +.wideMedium { + max-width: 770; +} + +.wideLandscape { + max-width: 662; +} diff --git a/packages/components/src/mobile/utils/alignments.native.js b/packages/components/src/mobile/utils/alignments.native.js new file mode 100644 index 0000000000000..a6541dc2713e9 --- /dev/null +++ b/packages/components/src/mobile/utils/alignments.native.js @@ -0,0 +1,14 @@ +export const WIDE_ALIGNMENTS = { + alignments: { + wide: 'wide', + full: 'full', + }, + supportedBlocks: [ 'core/cover', 'core/group', 'core/image' ], +}; + +export const ALIGNMENT_BREAKPOINTS = { + wide: 1024, + large: 820, + medium: 768, + small: 680, +}; diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 35ebfce94809a..edd8894dd922c 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -15,6 +15,7 @@ For each user feature we should also add a importance categorization label to i ## 1.37.0 * [**] Add support for rounded style in Image block +* [***] Full-width and wide alignment support for Group, Cover and Image block ## 1.36.0