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

Refactor the draggable component to avoid passing around unnecessary props #18756

Merged
merged 3 commits into from
Dec 5, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,18 @@ _Returns_

- `boolean`: Whether the caret is within formatted text.

<a name="isDraggingBlocks" href="#isDraggingBlocks">#</a> **isDraggingBlocks**

Returns true if the user is dragging blocks, or false otherwise.

_Parameters_

- _state_ `Object`: Global application state.

_Returns_

- `boolean`: Whether user is dragging blocks.

<a name="isFirstMultiSelectedBlock" href="#isFirstMultiSelectedBlock">#</a> **isFirstMultiSelectedBlock**

Returns true if a multi-selection exists, and the block corresponding to the
Expand Down Expand Up @@ -1173,6 +1185,14 @@ _Returns_

- `Object`: Action object.

<a name="startDraggingBlocks" href="#startDraggingBlocks">#</a> **startDraggingBlocks**

Returns an action object used in signalling that the user has begun to drag blocks.

_Returns_

- `Object`: Action object.

<a name="startMultiSelect" href="#startMultiSelect">#</a> **startMultiSelect**

Returns an action object used in signalling that a block multi-selection has started.
Expand All @@ -1189,6 +1209,14 @@ _Returns_

- `Object`: Action object.

<a name="stopDraggingBlocks" href="#stopDraggingBlocks">#</a> **stopDraggingBlocks**

Returns an action object used in signalling that the user has stopped dragging blocks.

_Returns_

- `Object`: Action object.

<a name="stopMultiSelect" href="#stopMultiSelect">#</a> **stopMultiSelect**

Returns an action object used in signalling that block multi-selection stopped.
Expand Down
71 changes: 57 additions & 14 deletions packages/block-editor/src/components/block-draggable/index.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,73 @@
/**
* External dependencies
*/
import { castArray } from 'lodash';

/**
* WordPress dependencies
*/
import { Draggable } from '@wordpress/components';
import { withSelect } from '@wordpress/data';
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';

const BlockDraggable = ( { children, clientIds } ) => {
const {
srcRootClientId,
index,
isDraggable,
} = useSelect( ( select ) => {
const {
getBlockIndex,
getBlockRootClientId,
getTemplateLock,
} = select( 'core/block-editor' );
const normalizedClientIds = castArray( clientIds );
const rootClientId = normalizedClientIds.length === 1 ? getBlockRootClientId( normalizedClientIds[ 0 ] ) : null;
const templateLock = rootClientId ? getTemplateLock( rootClientId ) : null;

return {
index: getBlockIndex( normalizedClientIds[ 0 ], rootClientId ),
srcRootClientId: rootClientId,
isDraggable: normalizedClientIds.length === 1 && 'all' !== templateLock,
};
}, [ clientIds ] );
const isDragging = useRef( false );
const { startDraggingBlocks, stopDraggingBlocks } = useDispatch( 'core/block-editor' );

const BlockDraggable = ( { children, clientId, rootClientId, blockElementId, index, onDragStart, onDragEnd } ) => {
// Stop dragging blocks if the block draggable is unmounted
useEffect( () => {
return () => {
if ( isDragging.current ) {
stopDraggingBlocks();
}
};
}, [] );

if ( ! isDraggable ) {
return null;
}

const normalizedClientIds = castArray( clientIds );
const blockElementId = `block-${ normalizedClientIds[ 0 ] }`;
const transferData = {
type: 'block',
srcIndex: index,
srcRootClientId: rootClientId,
srcClientId: clientId,
srcClientId: normalizedClientIds[ 0 ],
srcRootClientId,
};

return (
<Draggable
elementId={ blockElementId }
transferData={ transferData }
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
onDragStart={ () => {
startDraggingBlocks();
isDragging.current = true;
} }
onDragEnd={ () => {
stopDraggingBlocks();
isDragging.current = false;
} }
>
{
( { onDraggableStart, onDraggableEnd } ) => {
Expand All @@ -31,11 +81,4 @@ const BlockDraggable = ( { children, clientId, rootClientId, blockElementId, ind
);
};

export default withSelect( ( select, { clientId } ) => {
const { getBlockIndex, getBlockRootClientId } = select( 'core/block-editor' );
const rootClientId = getBlockRootClientId( clientId );
return {
index: getBlockIndex( clientId, rootClientId ),
rootClientId,
};
} )( BlockDraggable );
export default BlockDraggable;
29 changes: 10 additions & 19 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { __, sprintf } from '@wordpress/i18n';
import {
withDispatch,
withSelect,
useSelect,
} from '@wordpress/data';
import { withViewportMatch } from '@wordpress/viewport';
import { compose, pure, ifCondition } from '@wordpress/compose';
Expand Down Expand Up @@ -76,9 +77,7 @@ function BlockListBlock( {
isTypingWithinBlock,
isCaretWithinFormattedText,
isEmptyDefaultBlock,
isMovable,
isParentOfSelectedBlock,
isDraggable,
isSelectionEnabled,
className,
name,
Expand All @@ -103,6 +102,14 @@ function BlockListBlock( {
setNavigationMode,
isMultiSelecting,
} ) {
// In addition to withSelect, we should favor using useSelect in this component going forward
// to avoid leaking new props to the public API (editor.BlockListBlock filter)
const { isDraggingBlocks } = useSelect( ( select ) => {
youknowriad marked this conversation as resolved.
Show resolved Hide resolved
return {
isDraggingBlocks: select( 'core/block-editor' ).isDraggingBlocks(),
};
} );

// Random state used to rerender the component if needed, ideally we don't need this
const [ , updateRerenderState ] = useState( {} );
const rerender = () => updateRerenderState( {} );
Expand Down Expand Up @@ -168,15 +175,6 @@ function BlockListBlock( {
}
} );

// Handling the dragging state
const [ isDragging, setBlockDraggingState ] = useState( false );
const onDragStart = () => {
setBlockDraggingState( true );
};
const onDragEnd = () => {
setBlockDraggingState( false );
};

// Handling the error state
const [ hasError, setErrorState ] = useState( false );
const onBlockError = () => setErrorState( true );
Expand Down Expand Up @@ -457,6 +455,7 @@ function BlockListBlock( {
);

const shouldRenderDropzone = shouldShowInsertionPoint;
const isDragging = isDraggingBlocks && ( isSelected || isPartOfMultiSelection );
youknowriad marked this conversation as resolved.
Show resolved Hide resolved

// The wp-block className is important for editor styles.
// Generate the wrapper class names handling the different states of the block.
Expand Down Expand Up @@ -489,14 +488,7 @@ function BlockListBlock( {
const blockMover = (
<BlockMover
clientIds={ clientId }
blockElementId={ blockElementId }
isHidden={ ! isSelected }
isDraggable={
youknowriad marked this conversation as resolved.
Show resolved Hide resolved
isDraggable !== false &&
( ! isPartOfMultiSelection && isMovable )
}
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
__experimentalOrientation={ moverDirection }
/>
);
Expand Down Expand Up @@ -701,7 +693,6 @@ const applyWithSelect = withSelect(
initialPosition: isSelected ? getSelectedBlocksInitialCaretPosition() : null,
isEmptyDefaultBlock:
name && isUnmodifiedDefaultBlock( { name, attributes } ),
isMovable: 'all' !== templateLock,
isLocked: !! templateLock,
isFocusMode: focusMode && isLargeViewport,
hasFixedToolbar: hasFixedToolbar && isLargeViewport,
Expand Down
2 changes: 0 additions & 2 deletions packages/block-editor/src/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,6 @@ class BlockList extends Component {
blockClientIds,
rootClientId,
__experimentalMoverDirection: moverDirection = 'vertical',
isDraggable,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prop was available so alternative editor implementations could hide the drag icon if they needed (mobile?). Perhaps it's fine to remove, but wanted to flag in case it's not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How successful was that? Any editor you know ever used it?

selectedBlockClientId,
multiSelectedBlockClientIds,
hasMultiSelection,
Expand Down Expand Up @@ -250,7 +249,6 @@ class BlockList extends Component {
rootClientId={ rootClientId }
clientId={ clientId }
onSelectionStart={ this.onSelectionStart }
isDraggable={ isDraggable }
moverDirection={ moverDirection }
isMultiSelecting={ isMultiSelecting }
// This prop is explicitely computed and passed down
Expand Down
39 changes: 0 additions & 39 deletions packages/block-editor/src/components/block-mover/drag-handle.js

This file was deleted.

27 changes: 16 additions & 11 deletions packages/block-editor/src/components/block-mover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { withInstanceId, compose } from '@wordpress/compose';
*/
import { getBlockMoverDescription } from './mover-description';
import { leftArrow, rightArrow, upArrow, downArrow, dragHandle } from './icons';
import { IconDragHandle } from './drag-handle';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's on! :trollface:

Not a blocker and would definitely go with what you think is better given that I'm not very active lately. However, I still think IconDragHandle has a place here to hide how the DnD logic works and to maintain the same level of abstraction than the other icons. I find it more useful and fitting now that we've removed the onDragStart / onDragEnd boilerplate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it's not all good or bad no matter what we do here.

Personally, while working on it, I find the indirection unnecessary as I was forced to jump between three components (DragHandle, BlockDraggable and the Mover).

maintain the same level of abstraction than the other icons

What other icons you're talking about, I personally find it a bit more consistent this way as the Mover is rendering all its buttons in the same way (directly usiung <IconButton>)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What other icons you're talking about, I personally find it a bit more consistent this way as the Mover is rendering all its buttons in the same way (directly usiung )

I wish that was the case! But I can't help but feeling that the "children as function" pattern is still odd and IconDragHandle is a nice way to hide it from readers (until they are ready or need to dig in).

However, my concerns are lessened by the fact that, whatever drag component we use, it is not meant to be widely reusable, so I'm ok with this approach.

import BlockDraggable from '../block-draggable';

export class BlockMover extends Component {
constructor() {
Expand All @@ -44,7 +44,7 @@ export class BlockMover extends Component {
}

render() {
const { onMoveUp, onMoveDown, __experimentalOrientation: orientation, isRTL, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, isLocked, instanceId, isHidden, rootClientId } = this.props;
const { onMoveUp, onMoveDown, __experimentalOrientation: orientation, isRTL, isFirst, isLast, clientIds, blockType, firstIndex, isLocked, instanceId, isHidden, rootClientId } = this.props;
const { isFocused } = this.state;
const blocksCount = castArray( clientIds ).length;
if ( isLocked || ( isFirst && isLast && ! rootClientId ) ) {
Expand Down Expand Up @@ -98,15 +98,20 @@ export class BlockMover extends Component {
onFocus={ this.onFocus }
onBlur={ this.onBlur }
/>
<IconDragHandle
className="editor-block-mover__control block-editor-block-mover__control"
icon={ dragHandle }
clientId={ clientIds }
blockElementId={ blockElementId }
isVisible={ isDraggable }
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
/>

<BlockDraggable clientIds={ clientIds }>
{ ( { onDraggableStart, onDraggableEnd } ) => (
<IconButton
icon={ dragHandle }
className="block-editor-block-mover__control-drag-handle editor-block-mover__control block-editor-block-mover__control"
aria-hidden="true"
onDragStart={ onDraggableStart }
onDragEnd={ onDraggableEnd }
draggable
/>
) }
</BlockDraggable>

<IconButton
className="editor-block-mover__control block-editor-block-mover__control"
onClick={ isLast ? null : onMoveDown }
Expand Down
Loading