Skip to content

Commit

Permalink
Enable drag and drop in List View, fix performance issues (#33320)
Browse files Browse the repository at this point in the history
* Enable list view

* Use drop indicator component instead of borders

* Adjust positioning

* Unset other styles that interfere with the drop indicator

* Fix isDropBlock detection

* Fix flickering when nesting

* Fix positioning when dropping to nested blocks

* Avoid using context and improve some comments

* Move provider back

* Update naming in block navigation test

* Add basic e2e tests

* Update renamed test snapshot

* Simplify tests

* Remove delay

* Remove obsolete snapshot

* Fix incorrect drop indicator showing when two list views are open. Remove duplicate ids
  • Loading branch information
talldan authored Jul 13, 2021
1 parent 1fdc0f2 commit dd4ce77
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 121 deletions.
2 changes: 0 additions & 2 deletions packages/block-editor/src/components/block-draggable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const BlockDraggable = ( {
cloneClassname,
onDragStart,
onDragEnd,
elementId,
} ) => {
const { srcRootClientId, isDraggable, icon } = useSelect(
( select ) => {
Expand Down Expand Up @@ -75,7 +74,6 @@ const BlockDraggable = ( {
return (
<Draggable
cloneClassname={ cloneClassname }
elementId={ elementId }
__experimentalTransferDataType="wp-blocks"
transferData={ transferData }
onDragStart={ ( event ) => {
Expand Down
39 changes: 5 additions & 34 deletions packages/block-editor/src/components/list-view/block-contents.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,11 @@ const ListViewBlockContents = forwardRef(
},
ref
) => {
const {
__experimentalFeatures,
blockDropTarget = {},
} = useListViewContext();
const { __experimentalFeatures } = useListViewContext();

const { clientId } = block;

const {
rootClientId,
blockMovingClientId,
selectedBlockInBlockEditor,
} = useSelect(
const { blockMovingClientId, selectedBlockInBlockEditor } = useSelect(
( select ) => {
const {
getBlockRootClientId,
Expand All @@ -62,34 +55,12 @@ const ListViewBlockContents = forwardRef(
const isBlockMoveTarget =
blockMovingClientId && selectedBlockInBlockEditor === 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-list-view-block-contents', {
'is-dropping-before': isDroppingBefore || isBlockMoveTarget,
'is-dropping-after': isDroppingAfter,
'is-dropping-to-inner-blocks': isDroppingToInnerBlocks,
'is-dropping-before': isBlockMoveTarget,
} );

return (
<BlockDraggable
clientIds={ [ block.clientId ] }
elementId={ `list-view-block-${ block.clientId }` }
>
<BlockDraggable clientIds={ [ block.clientId ] }>
{ ( { draggable, onDragStart, onDragEnd } ) =>
__experimentalFeatures ? (
<ListViewBlockSlot
Expand Down Expand Up @@ -117,7 +88,7 @@ const ListViewBlockContents = forwardRef(
position={ position }
siblingBlockCount={ siblingBlockCount }
level={ level }
draggable={ draggable && __experimentalFeatures }
draggable={ draggable }
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
{ ...props }
Expand Down
125 changes: 125 additions & 0 deletions packages/block-editor/src/components/list-view/drop-indicator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* WordPress dependencies
*/
import { Popover } from '@wordpress/components';
import { useCallback, useMemo } from '@wordpress/element';

export default function ListViewDropIndicator( {
listViewRef,
blockDropTarget,
} ) {
const { rootClientId, clientId, dropPosition } = blockDropTarget || {};

const [ rootBlockElement, blockElement ] = useMemo( () => {
if ( ! listViewRef.current ) {
return [];
}

// The rootClientId will be defined whenever dropping into inner
// block lists, but is undefined when dropping at the root level.
const _rootBlockElement = rootClientId
? listViewRef.current.querySelector(
`[data-block="${ rootClientId }"]`
)
: undefined;

// The clientId represents the sibling block, the dragged block will
// usually be inserted adjacent to it. It will be undefined when
// dropping a block into an empty block list.
const _blockElement = clientId
? listViewRef.current.querySelector(
`[data-block="${ clientId }"]`
)
: undefined;

return [ _rootBlockElement, _blockElement ];
}, [ rootClientId, clientId ] );

// The targetElement is the element that the drop indicator will appear
// before or after. When dropping into an empty block list, blockElement
// is undefined, so the indicator will appear after the rootBlockElement.
const targetElement = blockElement || rootBlockElement;

const getDropIndicatorIndent = useCallback( () => {
if ( ! rootBlockElement ) {
return 0;
}

// Calculate the indent using the block icon of the root block.
// Using a classname selector here might be flaky and could be
// improved.
const targetElementRect = targetElement.getBoundingClientRect();
const rootBlockIconElement = rootBlockElement.querySelector(
'.block-editor-block-icon'
);
const rootBlockIconRect = rootBlockIconElement.getBoundingClientRect();
return rootBlockIconRect.right - targetElementRect.left;
}, [ rootBlockElement, targetElement ] );

const style = useMemo( () => {
if ( ! targetElement ) {
return {};
}

const indent = getDropIndicatorIndent();

return {
width: targetElement.offsetWidth - indent,
};
}, [ getDropIndicatorIndent, targetElement ] );

const getAnchorRect = useCallback( () => {
if ( ! targetElement ) {
return {};
}

const ownerDocument = targetElement.ownerDocument;
const rect = targetElement.getBoundingClientRect();
const indent = getDropIndicatorIndent();

const anchorRect = {
left: rect.left + indent,
right: rect.right,
width: 0,
height: rect.height,
ownerDocument,
};

if ( dropPosition === 'top' ) {
return {
...anchorRect,
top: rect.top,
bottom: rect.top,
};
}

if ( dropPosition === 'bottom' || dropPosition === 'inside' ) {
return {
...anchorRect,
top: rect.bottom,
bottom: rect.bottom,
};
}

return {};
}, [ targetElement, dropPosition, getDropIndicatorIndent ] );

if ( ! targetElement ) {
return null;
}

return (
<Popover
noArrow
animate={ false }
getAnchorRect={ getAnchorRect }
focusOnMount={ false }
className="block-editor-list-view-drop-indicator"
>
<div
style={ style }
className="block-editor-list-view-drop-indicator__line"
/>
</Popover>
);
}
50 changes: 27 additions & 23 deletions packages/block-editor/src/components/list-view/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* WordPress dependencies
*/

import { useMergeRefs } from '@wordpress/compose';
import { __experimentalTreeGrid as TreeGrid } from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import {
Expand All @@ -18,6 +19,7 @@ import { __ } from '@wordpress/i18n';
*/
import ListViewBranch from './branch';
import { ListViewContext } from './context';
import ListViewDropIndicator from './drop-indicator';
import useListViewClientIds from './use-list-view-client-ids';
import useListViewDropZone from './use-list-view-drop-zone';
import { store as blockEditorStore } from '../../store';
Expand Down Expand Up @@ -70,17 +72,15 @@ export default function ListView( {
);
const [ expandedState, setExpandedState ] = useReducer( expanded, {} );

let { ref: treeGridRef, target: blockDropTarget } = useListViewDropZone();
const { ref: dropZoneRef, target: blockDropTarget } = useListViewDropZone();
const elementRef = useRef();
const treeGridRef = useMergeRefs( [ elementRef, dropZoneRef ] );

const isMounted = useRef( false );
useEffect( () => {
isMounted.current = true;
}, [] );

if ( ! __experimentalFeatures ) {
blockDropTarget = undefined;
}

const expand = ( clientId ) => {
if ( ! clientId ) {
return;
Expand All @@ -104,7 +104,6 @@ export default function ListView( {
() => ( {
__experimentalFeatures,
__experimentalPersistentListViewFeatures,
blockDropTarget,
isTreeGridMounted: isMounted.current,
expandedState,
expand,
Expand All @@ -113,7 +112,6 @@ export default function ListView( {
[
__experimentalFeatures,
__experimentalPersistentListViewFeatures,
blockDropTarget,
isMounted.current,
expandedState,
expand,
Expand All @@ -122,21 +120,27 @@ export default function ListView( {
);

return (
<TreeGrid
className="block-editor-list-view-tree"
aria-label={ __( 'Block navigation structure' ) }
ref={ treeGridRef }
onCollapseRow={ collapseRow }
onExpandRow={ expandRow }
>
<ListViewContext.Provider value={ contextValue }>
<ListViewBranch
blocks={ clientIdsTree }
selectBlock={ selectEditorBlock }
selectedBlockClientIds={ selectedClientIds }
{ ...props }
/>
</ListViewContext.Provider>
</TreeGrid>
<>
<ListViewDropIndicator
listViewRef={ elementRef }
blockDropTarget={ blockDropTarget }
/>
<TreeGrid
className="block-editor-list-view-tree"
aria-label={ __( 'Block navigation structure' ) }
ref={ treeGridRef }
onCollapseRow={ collapseRow }
onExpandRow={ expandRow }
>
<ListViewContext.Provider value={ contextValue }>
<ListViewBranch
blocks={ clientIdsTree }
selectBlock={ selectEditorBlock }
selectedBlockClientIds={ selectedClientIds }
{ ...props }
/>
</ListViewContext.Provider>
</TreeGrid>
</>
);
}
55 changes: 33 additions & 22 deletions packages/block-editor/src/components/list-view/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,22 @@
&.is-selected .block-editor-list-view-block-contents {
background: var(--wp-admin-theme-color);
color: $white;

// Hide selection styles while a user is dragging blocks/files etc.
.is-dragging-components-draggable & {
background: none;
color: $gray-900;
}
}
&.is-selected .block-editor-list-view-block-contents:focus {
box-shadow:
inset 0 0 0 1px $white,
0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);

// Hide focus styles while a user is dragging blocks/files etc.
.is-dragging-components-draggable & {
box-shadow: none;
}
}

&.is-branch-selected.is-selected .block-editor-list-view-block-contents {
Expand Down Expand Up @@ -64,6 +75,11 @@
&:hover,
&:focus {
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);

// Hide hover styles while a user is dragging blocks/files etc.
.is-dragging-components-draggable & {
box-shadow: none;
}
}
&:focus {
z-index: 1;
Expand All @@ -80,28 +96,6 @@
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;
padding-right: 0;
Expand Down Expand Up @@ -318,3 +312,20 @@ $block-navigation-max-indent: 8;
transition: transform 0.2s ease;
@include reduce-motion("transition");
}

.block-editor-list-view-drop-indicator {
pointer-events: none;

.block-editor-list-view-drop-indicator__line {
background: var(--wp-admin-theme-color);
height: $border-width;
}
}

// Reset some popover defaults for the drop indicator.
.block-editor-list-view-drop-indicator:not([data-y-axis="middle"][data-x-axis="right"]) > .components-popover__content {
margin-left: 0;
border: none;
box-shadow: none;
}

Loading

0 comments on commit dd4ce77

Please sign in to comment.