Skip to content

Commit

Permalink
Add drag and drop to List View in Navigation Screen (#23952)
Browse files Browse the repository at this point in the history
* Split useBlockDropZone into multiple hooks

* Add Draggable to block navigation block component

* Add block navigation drop zone hook

* Allow tree grid component to take a ref

* Add drop zone to block navigation tree

* Allow custom elementId in BlockDraggable component

* Move block draggable to block contents and add simple initial drop indicator

* Fix block drop index

* Try allowing blocks to be nested when dragging below, first attempt

* Break early when cursor is contained by block

* Improve drop zone detection

* Fix issue where incorrect drop position selected when blocks overlap

* Improve drop zone indicators

* Hide dragged block in list view

* Refactor selector

* Hide dragged block children in list view

* Hide appender when dragging parent

* Fix block draggable chip error

* Refactor block drop event handlers into a single hook

* Disallow dropping in invalid block context

* Allow nesting from both above and below the insertion point

* remove unused data

* Fix comment

* Try a separate fixed position element for the drop indicator

* Update styles to improve drop zone indicator

* Fix media dragging and dropping

* Fix incorrect import

* Fix dropping blocks with no drop indicators

* Tidy up nesting code

* Fix useRef/useEffect anti-pattern, replace with useMemo

* Wrap up selectors in an object

* Convert getDropTargetBlocksData into a hook so that selectors can be colocated

* Add doc blocks

* Revert "Update styles to improve drop zone indicator"

This reverts commit 753008a.

* Avoid showing drop zone on dragged block

* Improve styles

* Try making the list view popover a modal drop zone

* Adjust drop indicator styles

* Use __experimentalFeatures to disable drag and drop outside of navigation editor

* Revert "Try making the list view popover a modal drop zone"

This reverts commit 2bc3dbb.

* Make selectors more succinct

* Change useMemo back to useEffect to ensure blocksData is recomputed correctly

* Fix incorrect conditional setting of target

* Specify all the deps

* Add comment
  • Loading branch information
talldan authored Sep 2, 2020
1 parent ab5cbfa commit 00f824a
Show file tree
Hide file tree
Showing 10 changed files with 516 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function BlockDraggableChip( { clientIds } ) {
const [ firstId ] = clientIds;
const blockName = getBlockName( firstId );

return getBlockType( blockName ).icon;
return getBlockType( blockName )?.icon;
},
[ clientIds ]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const BlockDraggable = ( {
cloneClassname,
onDragStart,
onDragEnd,
elementId,
} ) => {
const { srcRootClientId, isDraggable } = useSelect(
( select ) => {
Expand Down Expand Up @@ -68,7 +69,7 @@ const BlockDraggable = ( {
return (
<Draggable
cloneClassname={ cloneClassname }
elementId={ `block-${ clientIds[ 0 ] }` }
elementId={ elementId || `block-${ clientIds[ 0 ] }` }
transferData={ transferData }
onDragStart={ ( event ) => {
startDraggingBlocks( clientIds );
Expand Down
20 changes: 20 additions & 0 deletions packages/block-editor/src/components/block-navigation/appender.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { __experimentalTreeGridCell as TreeGridCell } from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
import { __, sprintf } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -20,6 +26,19 @@ export default function BlockNavigationAppender( {
terminatedLevels,
path,
} ) {
const isDragging = useSelect(
( select ) => {
const { isBlockBeingDragged, isAncestorBeingDragged } = select(
'core/block-editor'
);

return (
isBlockBeingDragged( parentBlockClientId ) ||
isAncestorBeingDragged( parentBlockClientId )
);
},
[ parentBlockClientId ]
);
const instanceId = useInstanceId( BlockNavigationAppender );
const descriptionId = `block-navigation-appender-row__description_${ instanceId }`;

Expand All @@ -32,6 +51,7 @@ export default function BlockNavigationAppender( {

return (
<BlockNavigationLeaf
className={ classnames( { 'is-dragging': isDragging } ) }
level={ level }
position={ position }
rowCount={ rowCount }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { forwardRef } from '@wordpress/element';

/**
Expand All @@ -9,6 +15,7 @@ import { forwardRef } from '@wordpress/element';
import { useBlockNavigationContext } from './context';
import BlockNavigationBlockSlot from './block-slot';
import BlockNavigationBlockSelectButton from './block-select-button';
import BlockDraggable from '../block-draggable';

const BlockNavigationBlockContents = forwardRef(
(
Expand All @@ -24,33 +31,85 @@ const BlockNavigationBlockContents = forwardRef(
ref
) => {
const {
__experimentalFeatures: withBlockNavigationSlots,
__experimentalFeatures,
blockDropTarget = {},
} = useBlockNavigationContext();

return withBlockNavigationSlots ? (
<BlockNavigationBlockSlot
ref={ ref }
className="block-editor-block-navigation-block-contents"
block={ block }
onClick={ onClick }
isSelected={ isSelected }
position={ position }
siblingBlockCount={ siblingBlockCount }
level={ level }
{ ...props }
/>
) : (
<BlockNavigationBlockSelectButton
ref={ ref }
className="block-editor-block-navigation-block-contents"
block={ block }
onClick={ onClick }
isSelected={ isSelected }
position={ position }
siblingBlockCount={ siblingBlockCount }
level={ level }
{ ...props }
/>
const { clientId } = block;

const rootClientId = useSelect(
( select ) =>
select( 'core/block-editor' ).getBlockRootClientId(
clientId
) || '',
[ clientId ]
);

const {
rootClientId: dropTargetRootClientId,
clientId: dropTargetClientId,
dropPosition,
} = blockDropTarget;

const isDroppingBefore =
dropTargetRootClientId === rootClientId &&
dropTargetClientId === clientId &&
dropPosition === 'top';
const isDroppingAfter =
dropTargetRootClientId === rootClientId &&
dropTargetClientId === clientId &&
dropPosition === 'bottom';
const isDroppingToInnerBlocks =
dropTargetRootClientId === clientId && dropPosition === 'inside';

const className = classnames(
'block-editor-block-navigation-block-contents',
{
'is-dropping-before': isDroppingBefore,
'is-dropping-after': isDroppingAfter,
'is-dropping-to-inner-blocks': isDroppingToInnerBlocks,
}
);

return (
<BlockDraggable
clientIds={ [ block.clientId ] }
elementId={ `block-navigation-block-${ block.clientId }` }
>
{ ( { isDraggable, onDraggableStart, onDraggableEnd } ) =>
__experimentalFeatures ? (
<BlockNavigationBlockSlot
ref={ ref }
className={ className }
block={ block }
onClick={ onClick }
isSelected={ isSelected }
position={ position }
siblingBlockCount={ siblingBlockCount }
level={ level }
draggable={ isDraggable && __experimentalFeatures }
onDragStart={ onDraggableStart }
onDragEnd={ onDraggableEnd }
{ ...props }
/>
) : (
<BlockNavigationBlockSelectButton
ref={ ref }
className={ className }
block={ block }
onClick={ onClick }
isSelected={ isSelected }
position={ position }
siblingBlockCount={ siblingBlockCount }
level={ level }
draggable={ isDraggable && __experimentalFeatures }
onDragStart={ onDraggableStart }
onDragEnd={ onDraggableEnd }
{ ...props }
/>
)
}
</BlockDraggable>
);
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ function BlockNavigationBlockSelectButton(
level,
tabIndex,
onFocus,
onDragStart,
onDragEnd,
draggable,
},
ref
) {
Expand Down Expand Up @@ -59,6 +62,9 @@ function BlockNavigationBlockSelectButton(
ref={ ref }
tabIndex={ tabIndex }
onFocus={ onFocus }
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
draggable={ draggable }
>
<BlockIcon icon={ blockType.icon } showColors />
{ blockDisplayName }
Expand Down
21 changes: 19 additions & 2 deletions packages/block-editor/src/components/block-navigation/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { __ } from '@wordpress/i18n';
import { moreVertical } from '@wordpress/icons';
import { useState, useRef, useEffect } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { useDispatch, useSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand Down Expand Up @@ -45,10 +45,24 @@ export default function BlockNavigationBlock( {
const cellRef = useRef( null );
const [ isHovered, setIsHovered ] = useState( false );
const [ isFocused, setIsFocused ] = useState( false );
const { clientId } = block;
const isDragging = useSelect(
( select ) => {
const { isBlockBeingDragged, isAncestorBeingDragged } = select(
'core/block-editor'
);

return (
isBlockBeingDragged( clientId ) ||
isAncestorBeingDragged( clientId )
);
},
[ clientId ]
);

const { selectBlock: selectEditorBlock } = useDispatch(
'core/block-editor'
);
const { clientId } = block;

const hasSiblings = siblingBlockCount > 0;
const hasRenderedMovers = showBlockMovers && hasSiblings;
Expand All @@ -74,6 +88,7 @@ export default function BlockNavigationBlock( {
<BlockNavigationLeaf
className={ classnames( {
'is-selected': isSelected,
'is-dragging': isDragging,
} ) }
onMouseEnter={ () => setIsHovered( true ) }
onMouseLeave={ () => setIsHovered( false ) }
Expand All @@ -83,6 +98,8 @@ export default function BlockNavigationBlock( {
position={ position }
rowCount={ rowCount }
path={ path }
id={ `block-navigation-block-${ clientId }` }
data-block={ clientId }
>
<TreeGridCell
className="block-editor-block-navigation-block__contents-cell"
Expand Down
42 changes: 40 additions & 2 deletions packages/block-editor/src/components/block-navigation/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,55 @@ $tree-item-height: 36px;
// Use position relative for row animation.
position: relative;

&.is-dragging {
display: none;
}

.block-editor-block-navigation-block-contents {
display: flex;
align-items: center;
width: calc(100% - 0.8em);
width: 100%;
height: auto;
padding: $grid-unit-15 6px;
padding: $grid-unit-20 6px;
margin-top: auto;
margin-bottom: auto;
text-align: left;
color: $dark-gray-600;
border-radius: 2px;
position: relative;

&.is-dropping-before::before {
content: "";
position: absolute;
pointer-events: none;
transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear;
top: -2px;
right: 0;
left: 0;
border-top: 4px solid var(--wp-admin-theme-color);
}

&.is-dropping-after::before {
content: "";
position: absolute;
pointer-events: none;
transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear;
bottom: -2px;
right: 0;
left: 0;
border-bottom: 4px solid var(--wp-admin-theme-color);
}

&.is-dropping-to-inner-blocks::before {
content: "";
position: absolute;
pointer-events: none;
transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear;
bottom: -2px;
right: 0;
left: $icon-size;
border-bottom: 4px solid var(--wp-admin-theme-color);
}

.components-modal__content & {
padding-left: 0;
Expand Down
14 changes: 12 additions & 2 deletions packages/block-editor/src/components/block-navigation/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
*/

import { __experimentalTreeGrid as TreeGrid } from '@wordpress/components';
import { useMemo } from '@wordpress/element';
import { useMemo, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import BlockNavigationBranch from './branch';
import { BlockNavigationContext } from './context';
import useBlockNavigationDropZone from './use-block-navigation-drop-zone';

/**
* Wrap `BlockNavigationRows` with `TreeGrid`. BlockNavigationRows is a
Expand All @@ -24,17 +25,26 @@ export default function BlockNavigationTree( {
__experimentalFeatures,
...props
} ) {
const treeGridRef = useRef();
let blockDropTarget = useBlockNavigationDropZone( treeGridRef );

if ( ! __experimentalFeatures ) {
blockDropTarget = undefined;
}

const contextValue = useMemo(
() => ( {
__experimentalFeatures,
blockDropTarget,
} ),
[ __experimentalFeatures ]
[ __experimentalFeatures, blockDropTarget ]
);

return (
<TreeGrid
className="block-editor-block-navigation-tree"
aria-label={ __( 'Block navigation structure' ) }
ref={ treeGridRef }
>
<BlockNavigationContext.Provider value={ contextValue }>
<BlockNavigationBranch { ...props } />
Expand Down
Loading

0 comments on commit 00f824a

Please sign in to comment.