Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RNMobile] Allow shrink toolbar #22132

Merged
merged 19 commits into from
May 22, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions packages/block-editor/src/components/block-list/block.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { View, Text, TouchableWithoutFeedback } from 'react-native';
/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { Component, createRef } from '@wordpress/element';
import { GlobalStylesContext } from '@wordpress/components';
import { withDispatch, withSelect } from '@wordpress/data';
import { compose, withPreferredColorScheme } from '@wordpress/compose';
Expand All @@ -29,6 +29,13 @@ class BlockListBlock extends Component {

this.insertBlocksAfter = this.insertBlocksAfter.bind( this );
this.onFocus = this.onFocus.bind( this );
this.getBlockWidth = this.getBlockWidth.bind( this );

this.state = {
blockWidth: 0,
};

this.blockMobileToolbar = createRef();
}

onFocus() {
Expand All @@ -47,6 +54,15 @@ class BlockListBlock extends Component {
}
}

getBlockWidth( { nativeEvent } ) {
const { layout } = nativeEvent;
const { blockWidth } = this.state;

if ( blockWidth !== layout.width ) {
this.setState( { blockWidth: layout.width } );
}
}

getBlockForType() {
return (
<GlobalStylesContext.Consumer>
Expand Down Expand Up @@ -78,6 +94,7 @@ class BlockListBlock extends Component {
contentStyle={ this.props.contentStyle }
onDeleteBlock={ this.props.onDeleteBlock }
/>
<View onLayout={ this.getBlockWidth } />
</GlobalStylesContext.Provider>
);
} }
Expand All @@ -95,8 +112,8 @@ class BlockListBlock extends Component {

render() {
const {
attributes,
blockType,
attributes = {},
blockType = {},
lukewalczak marked this conversation as resolved.
Show resolved Hide resolved
clientId,
icon,
isSelected,
Expand All @@ -114,6 +131,8 @@ class BlockListBlock extends Component {
isInnerBlockSelected,
} = this.props;

const { blockWidth } = this.state;

const accessibilityLabel = getAccessibleBlockLabel(
blockType,
attributes,
Expand Down Expand Up @@ -170,14 +189,21 @@ class BlockListBlock extends Component {
icon={ icon }
/>
) }
<View style={ styles.neutralToolbar }>
<View
style={ styles.neutralToolbar }
ref={ this.blockMobileToolbar }
>
{ isSelected && (
<BlockMobileToolbar
clientId={ clientId }
onDelete={ onDeleteBlock }
isStackedHorizontally={
isStackedHorizontally
}
blockWidth={ blockWidth }
blockMobileToolbarRef={
this.blockMobileToolbar.current
}
/>
) }
</View>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/**
* External dependencies
*/
import { Platform, findNodeHandle } from 'react-native';
import { partial, first, castArray, last, compact } from 'lodash';
/**
* WordPress dependencies
*/
import { ToolbarButton, Picker } from '@wordpress/components';
import { getBlockType, getDefaultBlockName } from '@wordpress/blocks';
import { __, sprintf } from '@wordpress/i18n';
import { withDispatch, withSelect } from '@wordpress/data';
import { withInstanceId, compose } from '@wordpress/compose';
import { moreHorizontalMobile, trash, cog } from '@wordpress/icons';
/**
* Internal dependencies
*/
import { getMoversSetup } from '../block-mover/mover-description';

const BlockActionsMenu = ( {
onDelete,
isStackedHorizontally,
wrapBlockSettings,
wrapBlockMover,
openGeneralSidebar,
onMoveDown,
onMoveUp,
isFirst,
isLast,
blockTitle,
isEmptyDefaultBlock,
blockMobileToolbarRef,
} ) => {
const moversOptions = { keys: [ 'icon', 'actionTitle' ], blockTitle };

const {
icon: { backward: backwardButtonIcon, forward: forwardButtonIcon },
actionTitle: {
backward: backwardButtonTitle,
forward: forwardButtonTitle,
},
} = getMoversSetup( isStackedHorizontally, moversOptions );

const deleteOption = {
id: 'deleteOption',
// translators: %s: block title e.g: "Paragraph".
label: sprintf( __( 'Remove %s' ), blockTitle ),
value: 'deleteOption',
icon: trash,
separated: true,
disabled: isEmptyDefaultBlock,
};

const settingsOption = {
id: 'settingsOption',
// translators: %s: block title e.g: "Paragraph".
label: sprintf( __( '%s Settings' ), blockTitle ),
value: 'settingsOption',
icon: cog,
};

const backwardButtonOption = {
id: 'backwardButtonOption',
label: backwardButtonTitle,
value: 'backwardButtonOption',
icon: backwardButtonIcon,
disabled: isFirst,
};

const forwardButtonOption = {
id: 'forwardButtonOption',
label: forwardButtonTitle,
value: 'forwardButtonOption',
icon: forwardButtonIcon,
disabled: isLast,
};

const options = compact( [
wrapBlockMover && backwardButtonOption,
wrapBlockMover && forwardButtonOption,
wrapBlockSettings && settingsOption,
deleteOption,
] );

function onPickerSelect( value ) {
if ( value === 'deleteOption' ) {
lukewalczak marked this conversation as resolved.
Show resolved Hide resolved
onDelete();
} else if ( value === 'settingsOption' ) {
openGeneralSidebar();
} else if ( value === 'forwardButtonOption' ) {
onMoveDown();
} else if ( value === 'backwardButtonOption' ) {
onMoveUp();
}
}

const disabledButtonIndices = options
.map( ( option, index ) => option.disabled && index + 1 )
.filter( Boolean );

const accessibilityHintIOS = __(
'Double tap to open Action Sheet with available options'
);
const accessibilityHintAndroid = __(
'Double tap to open Bottom Sheet with available options'
);
lukewalczak marked this conversation as resolved.
Show resolved Hide resolved

return (
<>
<ToolbarButton
title={ __( 'Open Block Actions Menu' ) }
onClick={ () => this.picker.presentPicker() }
lukewalczak marked this conversation as resolved.
Show resolved Hide resolved
icon={ moreHorizontalMobile }
extraProps={ {
hint:
Platform.OS === 'ios'
? accessibilityHintIOS
: accessibilityHintAndroid,
} }
/>
<Picker
ref={ ( instance ) => ( this.picker = instance ) }
options={ options }
onChange={ onPickerSelect }
destructiveButtonIndex={ options.length }
disabledButtonIndices={ disabledButtonIndices }
hideCancelButton={ Platform !== 'ios' }
lukewalczak marked this conversation as resolved.
Show resolved Hide resolved
anchor={
blockMobileToolbarRef
? findNodeHandle( blockMobileToolbarRef )
: undefined
}
/>
</>
);
};

export default compose(
withSelect( ( select, { clientIds } ) => {
const {
getBlockIndex,
getBlockRootClientId,
getBlockOrder,
getBlockName,
getBlock,
} = select( 'core/block-editor' );
const normalizedClientIds = castArray( clientIds );
const block = getBlock( normalizedClientIds );
const blockName = getBlockName( normalizedClientIds );
const blockType = getBlockType( blockName );
const blockTitle = blockType.title;
const firstClientId = first( normalizedClientIds );
const rootClientId = getBlockRootClientId( firstClientId );
const blockOrder = getBlockOrder( rootClientId );

const firstIndex = getBlockIndex( firstClientId, rootClientId );
const lastIndex = getBlockIndex(
last( normalizedClientIds ),
rootClientId
);

const isDefaultBlock = blockName === getDefaultBlockName();
const isEmptyContent = block.attributes.content === '';
const isExactlyOneBlock = blockOrder.length === 1;
const isEmptyDefaultBlock =
isExactlyOneBlock && isDefaultBlock && isEmptyContent;

return {
isFirst: firstIndex === 0,
isLast: lastIndex === blockOrder.length - 1,
rootClientId,
blockTitle,
isEmptyDefaultBlock,
};
} ),
withDispatch( ( dispatch, { clientIds, rootClientId } ) => {
const { moveBlocksDown, moveBlocksUp } = dispatch(
'core/block-editor'
);
const { openGeneralSidebar } = dispatch( 'core/edit-post' );

return {
onMoveDown: partial( moveBlocksDown, clientIds, rootClientId ),
onMoveUp: partial( moveBlocksUp, clientIds, rootClientId ),
openGeneralSidebar: () => openGeneralSidebar( 'edit-post/block' ),
};
} ),
withInstanceId
)( BlockActionsMenu );
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,60 @@ import { Keyboard, View } from 'react-native';
/**
* WordPress dependencies
*/
import { ToolbarButton } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { withDispatch, withSelect } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { trash } from '@wordpress/icons';

/**
* Internal dependencies
*/
import styles from './style.scss';
import BlockMover from '../block-mover';
import BlockActionsMenu from './block-actions-menu';
import { BlockSettingsButton } from '../block-settings';

const BREAKPOINTS = {
dratwas marked this conversation as resolved.
Show resolved Hide resolved
wrapSettings: 65,
wrapMover: 150,
};
const BlockMobileToolbar = ( {
clientId,
onDelete,
order,
isStackedHorizontally,
} ) => (
<View style={ styles.toolbar }>
<BlockMover
clientIds={ [ clientId ] }
isStackedHorizontally={ isStackedHorizontally }
/>
blockWidth,
blockMobileToolbarRef,
lukewalczak marked this conversation as resolved.
Show resolved Hide resolved
} ) => {
const wrapBlockSettings = blockWidth < BREAKPOINTS.wrapSettings;
const wrapBlockMover = blockWidth <= BREAKPOINTS.wrapMover;

<View style={ styles.spacer } />
return (
<View style={ styles.toolbar }>
{ ! wrapBlockMover && (
<BlockMover
clientIds={ [ clientId ] }
isStackedHorizontally={ isStackedHorizontally }
/>
) }

<BlockSettingsButton.Slot>
{ /* Render only one settings icon even if we have more than one fill - need for hooks with controls */ }
{ ( fills = [ null ] ) => fills[ 0 ] }
</BlockSettingsButton.Slot>
<View style={ styles.spacer } />

<ToolbarButton
title={ sprintf(
/* translators: accessibility text. %s: current block position (number). */
__( 'Remove block at row %s' ),
order + 1
{ ! wrapBlockSettings && (
<BlockSettingsButton.Slot>
{ /* Render only one settings icon even if we have more than one fill - need for hooks with controls */ }
{ ( fills = [ null ] ) => fills[ 0 ] }
</BlockSettingsButton.Slot>
) }
onClick={ onDelete }
icon={ trash }
extraProps={ { hint: __( 'Double tap to remove the block' ) } }
/>
</View>
);

<BlockActionsMenu
dratwas marked this conversation as resolved.
Show resolved Hide resolved
clientIds={ [ clientId ] }
wrapBlockMover={ wrapBlockMover }
wrapBlockSettings={ wrapBlockSettings }
isStackedHorizontally={ isStackedHorizontally }
onDelete={ onDelete }
blockMobileToolbarRef={ blockMobileToolbarRef }
/>
</View>
);
};

export default compose(
withSelect( ( select, { clientId } ) => {
Expand Down
Loading