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

Enable drag and drop in List View, fix performance issues #33320

Merged
merged 16 commits into from
Jul 13, 2021
Merged
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 }
Copy link
Contributor Author

@talldan talldan Jul 12, 2021

Choose a reason for hiding this comment

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

The elementId prop seems to be dead code, Draggable ignores it when __experimentalDragComponent is defined, and BlockDraggable has that prop hard-coded just below this line.

__experimentalTransferDataType="wp-blocks"
transferData={ transferData }
onDragStart={ ( event ) => {
Expand Down
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,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO - remove this and use the new drop indicator for the 'Move To' option.

Copy link
Contributor Author

@talldan talldan Jul 9, 2021

Choose a reason for hiding this comment

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

Hmm, I'm going to make this a follow up too. I think Move To needs some refactoring. It currently changes the selected block to determine where it should be inserted, but it should just use the insertionPoint too. It needs changing in both the main block editor and list view.

} );

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 }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO - try making List View use the Insertion Point state so that the indicators appear in both the main and list view block lists.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tried this out, but it does slow things down a bit, will push my explorations to a separate PR.

/>
<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