Skip to content

Commit

Permalink
Improve insertion point and drag-n-drop in the widgets screen (#32953)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin940726 authored and youknowriad committed Jun 25, 2021
1 parent 90a317b commit c65792b
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function useInsertionPoint( {
let _destinationRootClientId = rootClientId;
let _destinationIndex;

if ( insertionIndex ) {
if ( insertionIndex !== undefined ) {
// Insert into a specific index.
_destinationIndex = insertionIndex;
} else if ( clientId ) {
Expand Down
70 changes: 12 additions & 58 deletions packages/edit-widgets/src/blocks/widget-area/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
*/
import WidgetAreaInnerBlocks from './inner-blocks';
import { store as editWidgetsStore } from '../../../store';
import useIsDraggingWithin from './use-is-dragging-within';

/** @typedef {import('@wordpress/element').RefObject} RefObject */

Expand Down Expand Up @@ -68,16 +69,17 @@ export default function WidgetAreaEdit( {
// unmounted when the panel is collapsed. Unmounting legacy
// widgets may have unintended consequences (e.g. TinyMCE
// not being properly reinitialized)
<DisclosureContent visible={ opened }>
<div className="editor-styles-wrapper">
<EntityProvider
kind="root"
type="postType"
id={ `widget-area-${ id }` }
>
<WidgetAreaInnerBlocks />
</EntityProvider>
</div>
<DisclosureContent
className="wp-block-widget-area__panel-body-content"
visible={ opened }
>
<EntityProvider
kind="root"
type="postType"
id={ `widget-area-${ id }` }
>
<WidgetAreaInnerBlocks />
</EntityProvider>
</DisclosureContent>
) }
</PanelBody>
Expand Down Expand Up @@ -117,51 +119,3 @@ const useIsDragging = ( elementRef ) => {

return isDragging;
};

/**
* A React hook to determine if it's dragging within the target element.
*
* @param {RefObject<HTMLElement>} elementRef The target elementRef object.
*
* @return {boolean} Is dragging within the target element.
*/
const useIsDraggingWithin = ( elementRef ) => {
const [ isDraggingWithin, setIsDraggingWithin ] = useState( false );

useEffect( () => {
const { ownerDocument } = elementRef.current;

function handleDragStart( event ) {
// Check the first time when the dragging starts.
handleDragEnter( event );
}

// Set to false whenever the user cancel the drag event by either releasing the mouse or press Escape.
function handleDragEnd() {
setIsDraggingWithin( false );
}

function handleDragEnter( event ) {
// Check if the current target is inside the item element.
if ( elementRef.current.contains( event.target ) ) {
setIsDraggingWithin( true );
} else {
setIsDraggingWithin( false );
}
}

// Bind these events to the document to catch all drag events.
// Ideally, we can also use `event.relatedTarget`, but sadly that doesn't work in Safari.
ownerDocument.addEventListener( 'dragstart', handleDragStart );
ownerDocument.addEventListener( 'dragend', handleDragEnd );
ownerDocument.addEventListener( 'dragenter', handleDragEnter );

return () => {
ownerDocument.removeEventListener( 'dragstart', handleDragStart );
ownerDocument.removeEventListener( 'dragend', handleDragEnd );
ownerDocument.removeEventListener( 'dragenter', handleDragEnter );
};
}, [] );

return isDraggingWithin;
};
48 changes: 40 additions & 8 deletions packages/edit-widgets/src/blocks/widget-area/edit/inner-blocks.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,53 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { useEntityBlockEditor } from '@wordpress/core-data';
import { InnerBlocks } from '@wordpress/block-editor';
import {
InnerBlocks,
__experimentalUseInnerBlocksProps,
} from '@wordpress/block-editor';
import { useRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import useIsDraggingWithin from './use-is-dragging-within';

export default function WidgetAreaInnerBlocks() {
const [ blocks, onInput, onChange ] = useEntityBlockEditor(
'root',
'postType'
);
const innerBlocksRef = useRef();
const isDraggingWithinInnerBlocks = useIsDraggingWithin( innerBlocksRef );
const shouldHighlightDropZone = isDraggingWithinInnerBlocks;
// Using the experimental hook so that we can control the className of the element.
const innerBlocksProps = __experimentalUseInnerBlocksProps(
{ ref: innerBlocksRef },
{
value: blocks,
onInput,
onChange,
templateLock: false,
renderAppender: InnerBlocks.ButtonBlockAppender,
}
);

return (
<InnerBlocks
value={ blocks }
onInput={ onInput }
onChange={ onChange }
templateLock={ false }
renderAppender={ InnerBlocks.ButtonBlockAppender }
/>
<div
className={ classnames(
'wp-block-widget-area__inner-blocks block-editor-inner-blocks editor-styles-wrapper',
{
'wp-block-widget-area__highlight-drop-zone': shouldHighlightDropZone,
}
) }
>
<div { ...innerBlocksProps } />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* WordPress dependencies
*/
import { useState, useEffect } from '@wordpress/element';

/** @typedef {import('@wordpress/element').RefObject} RefObject */

/**
* A React hook to determine if it's dragging within the target element.
*
* @param {RefObject<HTMLElement>} elementRef The target elementRef object.
*
* @return {boolean} Is dragging within the target element.
*/
const useIsDraggingWithin = ( elementRef ) => {
const [ isDraggingWithin, setIsDraggingWithin ] = useState( false );

useEffect( () => {
const { ownerDocument } = elementRef.current;

function handleDragStart( event ) {
// Check the first time when the dragging starts.
handleDragEnter( event );
}

// Set to false whenever the user cancel the drag event by either releasing the mouse or press Escape.
function handleDragEnd() {
setIsDraggingWithin( false );
}

function handleDragEnter( event ) {
// Check if the current target is inside the item element.
if ( elementRef.current.contains( event.target ) ) {
setIsDraggingWithin( true );
} else {
setIsDraggingWithin( false );
}
}

// Bind these events to the document to catch all drag events.
// Ideally, we can also use `event.relatedTarget`, but sadly that doesn't work in Safari.
ownerDocument.addEventListener( 'dragstart', handleDragStart );
ownerDocument.addEventListener( 'dragend', handleDragEnd );
ownerDocument.addEventListener( 'dragenter', handleDragEnter );

return () => {
ownerDocument.removeEventListener( 'dragstart', handleDragStart );
ownerDocument.removeEventListener( 'dragend', handleDragEnd );
ownerDocument.removeEventListener( 'dragenter', handleDragEnter );
};
}, [] );

return isDraggingWithin;
};

export default useIsDraggingWithin;
59 changes: 45 additions & 14 deletions packages/edit-widgets/src/blocks/widget-area/editor.scss
Original file line number Diff line number Diff line change
@@ -1,41 +1,72 @@
$panel-title-height: 48px;

.wp-block[data-type="core/widget-area"] {
max-width: $widget-area-width;
margin-left: auto;
margin-right: auto;

.components-panel__body > .components-panel__body-title {
font-family: $default-font;
margin-bottom: 0;
margin: 0;
height: $panel-title-height;
// Create a stacking context and make sure it's higher is than the content.
// Since the inner blocks will be stretched to cover the whole panel,
// we still want the title to be interactive.
position: relative;
z-index: 1;
background: $white;
// z-index should be enough to create a new stacking context,
// unfortunately, Safari needs this to stop it from flickering while dragging.
// The reason behind that is still unknown, probably a bug in the browser.
transform: translateZ(0);

// Remove default hover background in panel title. See #25752.
&:hover {
background: inherit;
background: $white;
}
}

.components-panel__body .editor-styles-wrapper {
margin: 0 (-$grid-unit-20) (-$grid-unit-20) (-$grid-unit-20);
padding: $grid-unit-20;
}

.block-list-appender.wp-block {
width: initial;
}

// Override theme custom widths for blocks.
.editor-styles-wrapper .wp-block.wp-block.wp-block.wp-block.wp-block {
max-width: 100%;
}

.components-panel__body.is-opened {
padding: 0;
}
}

// Add some spacing above the inner blocks so that the block toolbar doesn't
// overlap the panel header.
.wp-block-widget-area > .components-panel__body > div > .editor-styles-wrapper > .block-editor-inner-blocks {
padding-top: $grid-unit-30;
.blocks-widgets-container .wp-block-widget-area__inner-blocks.editor-styles-wrapper {
margin: 0;
padding: 0;

// Ensure the widget area block lists have a minimum height so that it doesn't
// collapse to zero height when it has no blocks. When that happens the block
// can't be used as a drop target.
> .block-editor-block-list__layout {
// Stretch the inner-blocks container to cover the entire panel,
// so that dragging onto anywhere in it works.
margin-top: -$panel-title-height;
// Add some spacing above the inner blocks so that the block toolbar doesn't
// overlap the panel header.
padding: ($panel-title-height + $grid-unit-30) $grid-unit-20 $grid-unit-20;

// Ensure the widget area block lists have a minimum height so that it doesn't
// collapse to zero height when it has no blocks. When that happens the block
// can't be used as a drop target.
min-height: $grid-unit-40;
}
}

.wp-block-widget-area__highlight-drop-zone {
outline: var(--wp-admin-border-width-focus) solid var(--wp-admin-theme-color);
}

// Prevent "dragenter" event from firing when dragging onto the title component.
body.is-dragging-components-draggable .wp-block[data-type="core/widget-area"] .components-panel__body > .components-panel__body-title {
&,
* {
pointer-events: none;
}
}

0 comments on commit c65792b

Please sign in to comment.