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

Add horizontal option for the block movers #16615

Merged
merged 20 commits into from Oct 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -9,11 +9,11 @@ import { ifViewportMatches } from '@wordpress/viewport';
import BlockMover from '../block-mover';
import VisualEditorInserter from '../inserter';

function BlockMobileToolbar( { clientId } ) {
function BlockMobileToolbar( { clientId, moverDirection } ) {
return (
<div className="editor-block-list__block-mobile-toolbar block-editor-block-list__block-mobile-toolbar">
<VisualEditorInserter />
<BlockMover clientIds={ [ clientId ] } />
<BlockMover clientIds={ [ clientId ] } __experimentalOrientation={ moverDirection } />
</div>
);
}
Expand Down
42 changes: 27 additions & 15 deletions packages/block-editor/src/components/block-list/block.js
Expand Up @@ -67,6 +67,7 @@ function BlockListBlock( {
mode,
isFocusMode,
hasFixedToolbar,
moverDirection,
isLocked,
clientId,
rootClientId,
Expand Down Expand Up @@ -450,6 +451,20 @@ function BlockListBlock( {
};
}
const blockElementId = `block-${ clientId }`;
const blockMover = (
<BlockMover
clientIds={ clientId }
blockElementId={ blockElementId }
isHidden={ ! isSelected }
isDraggable={
isDraggable !== false &&
( ! isPartOfMultiSelection && isMovable )
}
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
__experimentalOrientation={ moverDirection }
/>
);

// We wrap the BlockEdit component in a div that hides it when editing in
// HTML mode. This allows us to render all of the ancillary pieces
Expand Down Expand Up @@ -511,22 +526,18 @@ function BlockListBlock( {
rootClientId={ rootClientId }
/>
{ isFirstMultiSelected && (
<BlockMultiControls rootClientId={ rootClientId } />
<BlockMultiControls
rootClientId={ rootClientId }
moverDirection={ moverDirection }
/>
) }
<div className="editor-block-list__block-edit block-editor-block-list__block-edit">
{ shouldRenderMovers && (
<BlockMover
clientIds={ clientId }
blockElementId={ blockElementId }
isHidden={ ! isSelected }
isDraggable={
isDraggable !== false &&
( ! isPartOfMultiSelection && isMovable )
}
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
/>
<div
className={ classnames(
'editor-block-list__block-edit block-editor-block-list__block-edit',
{ 'has-mover-inside': moverDirection === 'horizontal' },
) }
>
{ shouldRenderMovers && ( moverDirection === 'vertical' ) && blockMover }
{ shouldShowBreadcrumb && (
<BlockBreadcrumb
clientId={ clientId }
Expand Down Expand Up @@ -566,6 +577,7 @@ function BlockListBlock( {
{ isValid && mode === 'html' && (
<BlockHtml clientId={ clientId } />
) }
{ shouldRenderMovers && ( moverDirection === 'horizontal' ) && blockMover }
{ ! isValid && [
<BlockInvalidWarning
key="invalid-warning"
Expand All @@ -578,7 +590,7 @@ function BlockListBlock( {
</BlockCrashBoundary>
{ !! hasError && <BlockCrashWarning /> }
{ shouldShowMobileToolbar && (
<BlockMobileToolbar clientId={ clientId } />
<BlockMobileToolbar clientId={ clientId } moverDirection={ moverDirection } />
) }
</IgnoreNestedEvents>
</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/block-editor/src/components/block-list/index.js
Expand Up @@ -195,6 +195,7 @@ class BlockList extends Component {
className,
blockClientIds,
rootClientId,
__experimentalMoverDirection: moverDirection = 'vertical',
isDraggable,
selectedBlockClientId,
multiSelectedBlockClientIds,
Expand Down Expand Up @@ -227,6 +228,7 @@ class BlockList extends Component {
blockRef={ this.setBlockRef }
onSelectionStart={ this.onSelectionStart }
isDraggable={ isDraggable }
moverDirection={ moverDirection }

// This prop is explicitely computed and passed down
// to avoid being impacted by the async mode
Expand Down
Expand Up @@ -11,6 +11,7 @@ import BlockMover from '../block-mover';
function BlockListMultiControls( {
multiSelectedBlockClientIds,
isSelecting,
moverDirection,
} ) {
if ( isSelecting ) {
return null;
Expand All @@ -19,6 +20,7 @@ function BlockListMultiControls( {
return (
<BlockMover
clientIds={ multiSelectedBlockClientIds }
__experimentalOrientation={ moverDirection }
/>
);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/block-editor/src/components/block-list/style.scss
Expand Up @@ -101,6 +101,9 @@

.block-editor-block-list__block-edit {
position: relative;
&.has-mover-inside > [data-block] {
display: flex;
}

&::before {
z-index: z-index(".block-editor-block-list__block-edit::before");
Expand Down
12 changes: 12 additions & 0 deletions packages/block-editor/src/components/block-mover/icons.js
Expand Up @@ -9,12 +9,24 @@ export const upArrow = (
</SVG>
);

export const leftArrow = (
<SVG width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
<Path d="M4.5 9l5.6-5.7 1.4 1.5L7.3 9l4.2 4.2-1.4 1.5L4.5 9z" />
</SVG>
);

export const downArrow = (
<SVG width="18" height="18" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
<Polygon points="9,13.5 14.7,7.9 13.2,6.5 9,10.7 4.8,6.5 3.3,7.9 " />
</SVG>
);

export const rightArrow = (
<SVG width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
<Path d="M13.5 9L7.9 3.3 6.5 4.8 10.7 9l-4.2 4.2 1.4 1.5L13.5 9z" />
</SVG>
);

export const dragHandle = (
<SVG width="18" height="18" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
<Path d="M13,8c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S12.4,8,13,8z M5,6C4.4,6,4,6.4,4,7s0.4,1,1,1s1-0.4,1-1S5.6,6,5,6z M5,10
Expand Down
57 changes: 49 additions & 8 deletions packages/block-editor/src/components/block-mover/index.js
Expand Up @@ -7,7 +7,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { __, sprintf } from '@wordpress/i18n';
import { IconButton } from '@wordpress/components';
import { getBlockType } from '@wordpress/blocks';
import { Component } from '@wordpress/element';
Expand All @@ -18,7 +18,7 @@ import { withInstanceId, compose } from '@wordpress/compose';
* Internal dependencies
*/
import { getBlockMoverDescription } from './mover-description';
import { upArrow, downArrow, dragHandle } from './icons';
import { leftArrow, rightArrow, upArrow, downArrow, dragHandle } from './icons';
import { IconDragHandle } from './drag-handle';

export class BlockMover extends Component {
Expand All @@ -44,24 +44,55 @@ export class BlockMover extends Component {
}

render() {
const { onMoveUp, onMoveDown, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, isLocked, instanceId, isHidden, rootClientId } = this.props;
const { onMoveUp, onMoveDown, __experimentalOrientation: orientation, isRTL, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, isLocked, instanceId, isHidden, rootClientId } = this.props;
const { isFocused } = this.state;
const blocksCount = castArray( clientIds ).length;
if ( isLocked || ( isFirst && isLast && ! rootClientId ) ) {
return null;
}

const getArrowIcon = ( moveDirection ) => {
if ( moveDirection === 'up' ) {
if ( orientation === 'horizontal' ) {
return isRTL ? rightArrow : leftArrow;
}
return upArrow;
} else if ( moveDirection === 'down' ) {
if ( orientation === 'horizontal' ) {
return isRTL ? leftArrow : rightArrow;
}
return downArrow;
}
return null;
};
draganescu marked this conversation as resolved.
Show resolved Hide resolved

const getMovementDirection = ( moveDirection ) => {
if ( moveDirection === 'up' ) {
if ( orientation === 'horizontal' ) {
return isRTL ? 'right' : 'left';
}
return 'up';
} else if ( moveDirection === 'down' ) {
if ( orientation === 'horizontal' ) {
return isRTL ? 'left' : 'right';
}
return 'down';
}
return null;
};
draganescu marked this conversation as resolved.
Show resolved Hide resolved

// We emulate a disabled state because forcefully applying the `disabled`
// attribute on the button while it has focus causes the screen to change
// to an unfocused state (body as active element) without firing blur on,
// the rendering parent, leaving it unable to react to focus out.
return (
<div className={ classnames( 'editor-block-mover block-editor-block-mover', { 'is-visible': isFocused || ! isHidden } ) }>
<div className={ classnames( 'editor-block-mover block-editor-block-mover', { 'is-visible': isFocused || ! isHidden, 'is-horizontal': orientation === 'horizontal' } ) }>
<IconButton
className="editor-block-mover__control block-editor-block-mover__control"
onClick={ isFirst ? null : onMoveUp }
icon={ upArrow }
label={ __( 'Move up' ) }
icon={ getArrowIcon( 'up' ) }
// translators: %s: Horizontal direction of block movement ( left, right )
label={ sprintf( __( 'Move %s' ), getMovementDirection( 'up' ) ) }
aria-describedby={ `block-editor-block-mover__up-description-${ instanceId }` }
draganescu marked this conversation as resolved.
Show resolved Hide resolved
aria-disabled={ isFirst }
onFocus={ this.onFocus }
Expand All @@ -79,8 +110,9 @@ export class BlockMover extends Component {
<IconButton
className="editor-block-mover__control block-editor-block-mover__control"
onClick={ isLast ? null : onMoveDown }
icon={ downArrow }
label={ __( 'Move down' ) }
icon={ getArrowIcon( 'down' ) }
// translators: %s: Horizontal direction of block movement ( left, right )
label={ sprintf( __( 'Move %s' ), getMovementDirection( 'down' ) ) }
aria-describedby={ `block-editor-block-mover__down-description-${ instanceId }` }
aria-disabled={ isLast }
onFocus={ this.onFocus }
Expand All @@ -95,6 +127,8 @@ export class BlockMover extends Component {
isFirst,
isLast,
-1,
orientation,
isRTL,
)
}
</span>
Expand All @@ -107,6 +141,8 @@ export class BlockMover extends Component {
isFirst,
isLast,
1,
orientation,
isRTL,
)
}
</span>
Expand All @@ -125,12 +161,17 @@ export default compose(
const blockOrder = getBlockOrder( rootClientId );
const firstIndex = getBlockIndex( firstClientId, rootClientId );
const lastIndex = getBlockIndex( last( normalizedClientIds ), rootClientId );
const { getSettings } = select( 'core/block-editor' );
const {
isRTL,
} = getSettings();

return {
blockType: block ? getBlockType( block.name ) : null,
isLocked: getTemplateLock( rootClientId ) === 'all',
rootClientId,
firstIndex,
isRTL,
isFirst: firstIndex === 0,
isLast: lastIndex === blockOrder.length - 1,
};
Expand Down
Expand Up @@ -14,12 +14,30 @@ import { __, _n, sprintf } from '@wordpress/i18n';
* @param {boolean} isLast This is the last block.
* @param {number} dir Direction of movement (> 0 is considered to be going
* down, < 0 is up).
* @param {string} orientation The orientation of the block movers, vertical or
* horizontal.
* @param {boolean} isRTL True if current writing system is right to left.
*
* @return {string} Label for the block movement controls.
*/
export function getBlockMoverDescription( selectedCount, type, firstIndex, isFirst, isLast, dir ) {
export function getBlockMoverDescription( selectedCount, type, firstIndex, isFirst, isLast, dir, orientation, isRTL ) {
const position = ( firstIndex + 1 );

const getMovementDirection = ( moveDirection ) => {
if ( moveDirection === 'up' ) {
if ( orientation === 'horizontal' ) {
return isRTL ? 'right' : 'left';
}
return 'up';
} else if ( moveDirection === 'down' ) {
if ( orientation === 'horizontal' ) {
return isRTL ? 'left' : 'right';
}
return 'down';
}
return null;
};
draganescu marked this conversation as resolved.
Show resolved Hide resolved

if ( selectedCount > 1 ) {
return getMultiBlockMoverDescription( selectedCount, firstIndex, isFirst, isLast, dir );
}
Expand All @@ -32,35 +50,46 @@ export function getBlockMoverDescription( selectedCount, type, firstIndex, isFir
if ( dir > 0 && ! isLast ) {
// moving down
return sprintf(
// translators: 1: Type of block (i.e. Text, Image etc), 2: Position of selected block, 3: New position
__( 'Move %1$s block from position %2$d down to position %3$d' ),
// translators: 1: Type of block (i.e. Text, Image etc), 2: Position of selected block, 3: Direction of movement ( up, down, left, right ), 4: New position
__( 'Move %1$s block from position %2$d %3$s to position %4$d' ),
type,
position,
( position + 1 )
getMovementDirection( 'down' ),
( position + 1 ),
);
}

if ( dir > 0 && isLast ) {
// moving down, and is the last item
// translators: %s: Type of block (i.e. Text, Image etc)
return sprintf( __( 'Block %s is at the end of the content and can’t be moved down' ), type );
// translators: 1: Type of block (i.e. Text, Image etc), 2: Direction of movement ( up, down, left, right )
return sprintf(
__( 'Block %1$s is at the end of the content and can’t be moved %2$s' ),
type,
getMovementDirection( 'down' ),

);
}

if ( dir < 0 && ! isFirst ) {
// moving up
return sprintf(
// translators: 1: Type of block (i.e. Text, Image etc), 2: Position of selected block, 3: New position
__( 'Move %1$s block from position %2$d up to position %3$d' ),
// translators: 1: Type of block (i.e. Text, Image etc), 2: Position of selected block, 3: Direction of movement ( up, down, left, right ), 4: New position
__( 'Move %1$s block from position %2$d %3$s to position %4$d' ),
type,
position,
( position - 1 )
getMovementDirection( 'up' ),
( position - 1 ),
);
}

if ( dir < 0 && isFirst ) {
// moving up, and is the first item
// translators: %s: Type of block (i.e. Text, Image etc)
return sprintf( __( 'Block %s is at the beginning of the content and can’t be moved up' ), type );
// translators: 1: Type of block (i.e. Text, Image etc), 2: Direction of movement ( up, down, left, right )
return sprintf(
__( 'Block %1$s is at the beginning of the content and can’t be moved %2$s' ),
type,
getMovementDirection( 'up' ),
);
}
}

Expand Down