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

List View: Explore alternative for WYSIWYG drag chip idea #60623

Draft
wants to merge 1 commit into
base: trunk
Choose a base branch
from
Draft
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 @@ -22,6 +22,7 @@ const BlockDraggable = ( {
clientIds,
cloneClassname,
elementId,
getFinalPosition,
onDragStart,
onDragEnd,
fadeWhenDisabled = false,
Expand Down Expand Up @@ -184,9 +185,11 @@ const BlockDraggable = ( {

return (
<Draggable
animateToFinalPosition
appendToOwnerDocument={ appendToOwnerDocument }
cloneClassname={ cloneClassname }
__experimentalTransferDataType="wp-blocks"
getFinalPosition={ getFinalPosition }
transferData={ transferData }
onDragStart={ ( event ) => {
// Defer hiding the dragged source element to the next
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { forwardRef } from '@wordpress/element';
import { forwardRef, useCallback } from '@wordpress/element';

/**
* Internal dependencies
Expand Down Expand Up @@ -47,8 +47,14 @@ const ListViewBlockContents = forwardRef(
[]
);

const { AdditionalBlockContent, insertedBlock, setInsertedBlock } =
useListViewContext();
const {
AdditionalBlockContent,
draggedClientIds,
insertedBlock,
listViewInstanceId,
setInsertedBlock,
treeGridElementRef,
} = useListViewContext();

const isBlockMoveTarget =
blockMovingClientId && selectedBlockInBlockEditor === clientId;
Expand All @@ -65,6 +71,15 @@ const ListViewBlockContents = forwardRef(
? selectedClientIds
: [ clientId ];

// TODO: This function could be moved further up, as it doesn't need to be defined on each block.
const getFinalPosition = useCallback( () => {
const targetElem = treeGridElementRef.current?.querySelector(
`[role=row][data-block="${ draggedClientIds[ 0 ] }"]`
);

return targetElem?.getBoundingClientRect();
}, [ draggedClientIds, treeGridElementRef ] );

return (
<>
{ AdditionalBlockContent && (
Expand All @@ -78,6 +93,9 @@ const ListViewBlockContents = forwardRef(
appendToOwnerDocument
clientIds={ draggableClientIds }
cloneClassname={ 'block-editor-list-view-draggable-chip' }
dragComponent={ null }
elementId={ `list-view-${ listViewInstanceId }-block-${ clientId }` }
getFinalPosition={ getFinalPosition }
>
{ ( { draggable, onDragStart, onDragEnd } ) => (
<ListViewBlockSelectButton
Expand Down
18 changes: 11 additions & 7 deletions packages/block-editor/src/components/list-view/leaf.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { forwardRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import useMovingAnimation from '../use-moving-animation';
// import useMovingAnimation from '../use-moving-animation';

const AnimatedTreeGridRow = animated( TreeGridRow );

Expand All @@ -33,13 +33,17 @@ const ListViewLeaf = forwardRef(
},
ref
) => {
const animationRef = useMovingAnimation( {
clientId: props[ 'data-block' ],
enableAnimation: true,
triggerAnimationOnChange: path,
} );
// TODO: See if we can enable the moving animation when
// rearranging via the editor canvas, but _not_ when dragging and dropping
// within the list view directly.

const mergedRef = useMergeRefs( [ ref, animationRef ] );
// const animationRef = useMovingAnimation( {
// clientId: props[ 'data-block' ],
// enableAnimation: true,
// triggerAnimationOnChange: path,
// } );

const mergedRef = useMergeRefs( [ ref ] );

return (
<AnimatedTreeGridRow
Expand Down
54 changes: 53 additions & 1 deletion packages/block-editor/src/components/list-view/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,59 @@
$block-navigation-max-indent: 8;

.block-editor-list-view-draggable-chip {
opacity: 0.8;
.block-editor-list-view-leaf {
background-color: $white;
border-radius: $radius-block-ui;
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
display: flex;
height: 36px;
// Where possible, restrict the width of the cloned row to the width of the list view.
max-width: 338px;
opacity: 1;

// Reset colors to use the admin color.
td {
background: none !important;
.block-editor-list-view-block-select-button,
.block-editor-block-icon,
.components-button.has-icon {
color: var(--wp-admin-theme-color) !important;
}
}

.block-editor-list-view__expander {
// Remove indent on the expander, as the dragged component offsets the entire row.
margin-left: 0 !important;
}

// Apply a margin offset to account for nesting level.
&[aria-level] {
margin-left: ( $icon-size ) * $block-navigation-max-indent + 4 * ( $block-navigation-max-indent - 1 );
}

// When updating the margin for each indentation level, the corresponding
// indentation in `use-list-view-drop-zone.js` must be updated as well
// to ensure the drop zone is aligned with the indentation.
@for $i from 0 to $block-navigation-max-indent {
&[aria-level="#{ $i + 1 }"] {
@if $i - 1 >= 0 {
margin-left: ( $icon-size * $i ) + 4 * ($i - 1) !important;
}
@else {
margin-left: ( $icon-size * $i ) !important;
}
}
}

.block-editor-list-view-block__contents-cell {
flex: 1;
}

.block-editor-list-view-block__menu-cell {
display: flex;
align-items: center;
}
}
}

.block-editor-list-view-block__contents-cell,
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/animation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

// eslint-disable-next-line no-restricted-imports
export {
animate as __unstableAnimate,
motion as __unstableMotion,
AnimatePresence as __unstableAnimatePresence,
MotionContext as __unstableMotionContext,
Expand Down
42 changes: 41 additions & 1 deletion packages/components/src/draggable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useEffect, useRef } from '@wordpress/element';
* Internal dependencies
*/
import type { DraggableProps } from './types';
import { __unstableAnimate as animate } from '../animation';

const dragImageClass = 'components-draggable__invisible-drag-image';
const cloneWrapperClass = 'components-draggable__clone';
Expand Down Expand Up @@ -63,14 +64,17 @@ export function Draggable( {
onDragStart,
onDragOver,
onDragEnd,
animateToFinalPosition = false,
appendToOwnerDocument = false,
getFinalPosition,
cloneClassname,
elementId,
transferData,
__experimentalTransferDataType: transferDataType = 'text',
__experimentalDragComponent: dragComponent,
}: DraggableProps ) {
const dragComponentRef = useRef< HTMLDivElement >( null );
const cloneWrapperRef = useRef< Element | null >( null );
const cleanup = useRef( () => {} );

/**
Expand All @@ -80,8 +84,42 @@ export function Draggable( {
*/
function end( event: DragEvent ) {
event.preventDefault();
cleanup.current();

// TODO: should check if prefers reduced motion is active

if (
animateToFinalPosition &&
getFinalPosition &&
cloneWrapperRef.current
) {
const finalPosition = getFinalPosition();
const currentPosition =
cloneWrapperRef.current?.getBoundingClientRect();

if ( finalPosition && currentPosition ) {
const elem: Element = cloneWrapperRef.current;

animate(
elem,
{
x: [ currentPosition.x, finalPosition.x ],
y: [ currentPosition.y, finalPosition.y ],
},
{
duration: 0.2,
onComplete: () => {
cleanup.current();
if ( onDragEnd ) {
onDragEnd( event );
}
},
}
);
return;
}
}

cleanup.current();
if ( onDragEnd ) {
onDragEnd( event );
}
Expand Down Expand Up @@ -110,6 +148,8 @@ export function Draggable( {
cloneWrapper.style.top = '0';
cloneWrapper.style.left = '0';

cloneWrapperRef.current = cloneWrapper;

const dragImage = ownerDocument.createElement( 'div' );

// Set a fake drag image to avoid browser defaults. Remove from DOM
Expand Down
10 changes: 10 additions & 0 deletions packages/components/src/draggable/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export type DraggableProps = {
*/
onDraggableEnd: ( event: DragEvent ) => void;
} ) => JSX.Element | null;
/**
* Whether to animate the cloned element to its final position.
*
* @default false
*/
animateToFinalPosition?: boolean;
/**
* Whether to append the cloned element to the `ownerDocument` body.
* By default, elements sourced by id are appended to the element's wrapper.
Expand All @@ -32,6 +38,10 @@ export type DraggableProps = {
* The HTML id of the element to clone on drag
*/
elementId: string;
/**
* A function that returns the final position for the cloned element.
*/
getFinalPosition?: () => { x: number; y: number } | null;
/**
* A function called when dragging ends. This callback receives the `event`
* object from the `dragend` event as its first parameter.
Expand Down
Loading