Skip to content

Commit c65792b

Browse files
kevin940726youknowriad
authored andcommitted
Improve insertion point and drag-n-drop in the widgets screen (#32953)
1 parent 90a317b commit c65792b

File tree

5 files changed

+154
-81
lines changed

5 files changed

+154
-81
lines changed

packages/block-editor/src/components/inserter/hooks/use-insertion-point.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ function useInsertionPoint( {
5959
let _destinationRootClientId = rootClientId;
6060
let _destinationIndex;
6161

62-
if ( insertionIndex ) {
62+
if ( insertionIndex !== undefined ) {
6363
// Insert into a specific index.
6464
_destinationIndex = insertionIndex;
6565
} else if ( clientId ) {

packages/edit-widgets/src/blocks/widget-area/edit/index.js

Lines changed: 12 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
*/
1616
import WidgetAreaInnerBlocks from './inner-blocks';
1717
import { store as editWidgetsStore } from '../../../store';
18+
import useIsDraggingWithin from './use-is-dragging-within';
1819

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

@@ -68,16 +69,17 @@ export default function WidgetAreaEdit( {
6869
// unmounted when the panel is collapsed. Unmounting legacy
6970
// widgets may have unintended consequences (e.g. TinyMCE
7071
// not being properly reinitialized)
71-
<DisclosureContent visible={ opened }>
72-
<div className="editor-styles-wrapper">
73-
<EntityProvider
74-
kind="root"
75-
type="postType"
76-
id={ `widget-area-${ id }` }
77-
>
78-
<WidgetAreaInnerBlocks />
79-
</EntityProvider>
80-
</div>
72+
<DisclosureContent
73+
className="wp-block-widget-area__panel-body-content"
74+
visible={ opened }
75+
>
76+
<EntityProvider
77+
kind="root"
78+
type="postType"
79+
id={ `widget-area-${ id }` }
80+
>
81+
<WidgetAreaInnerBlocks />
82+
</EntityProvider>
8183
</DisclosureContent>
8284
) }
8385
</PanelBody>
@@ -117,51 +119,3 @@ const useIsDragging = ( elementRef ) => {
117119

118120
return isDragging;
119121
};
120-
121-
/**
122-
* A React hook to determine if it's dragging within the target element.
123-
*
124-
* @param {RefObject<HTMLElement>} elementRef The target elementRef object.
125-
*
126-
* @return {boolean} Is dragging within the target element.
127-
*/
128-
const useIsDraggingWithin = ( elementRef ) => {
129-
const [ isDraggingWithin, setIsDraggingWithin ] = useState( false );
130-
131-
useEffect( () => {
132-
const { ownerDocument } = elementRef.current;
133-
134-
function handleDragStart( event ) {
135-
// Check the first time when the dragging starts.
136-
handleDragEnter( event );
137-
}
138-
139-
// Set to false whenever the user cancel the drag event by either releasing the mouse or press Escape.
140-
function handleDragEnd() {
141-
setIsDraggingWithin( false );
142-
}
143-
144-
function handleDragEnter( event ) {
145-
// Check if the current target is inside the item element.
146-
if ( elementRef.current.contains( event.target ) ) {
147-
setIsDraggingWithin( true );
148-
} else {
149-
setIsDraggingWithin( false );
150-
}
151-
}
152-
153-
// Bind these events to the document to catch all drag events.
154-
// Ideally, we can also use `event.relatedTarget`, but sadly that doesn't work in Safari.
155-
ownerDocument.addEventListener( 'dragstart', handleDragStart );
156-
ownerDocument.addEventListener( 'dragend', handleDragEnd );
157-
ownerDocument.addEventListener( 'dragenter', handleDragEnter );
158-
159-
return () => {
160-
ownerDocument.removeEventListener( 'dragstart', handleDragStart );
161-
ownerDocument.removeEventListener( 'dragend', handleDragEnd );
162-
ownerDocument.removeEventListener( 'dragenter', handleDragEnter );
163-
};
164-
}, [] );
165-
166-
return isDraggingWithin;
167-
};
Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,53 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import classnames from 'classnames';
5+
16
/**
27
* WordPress dependencies
38
*/
49
import { useEntityBlockEditor } from '@wordpress/core-data';
5-
import { InnerBlocks } from '@wordpress/block-editor';
10+
import {
11+
InnerBlocks,
12+
__experimentalUseInnerBlocksProps,
13+
} from '@wordpress/block-editor';
14+
import { useRef } from '@wordpress/element';
15+
16+
/**
17+
* Internal dependencies
18+
*/
19+
import useIsDraggingWithin from './use-is-dragging-within';
620

721
export default function WidgetAreaInnerBlocks() {
822
const [ blocks, onInput, onChange ] = useEntityBlockEditor(
923
'root',
1024
'postType'
1125
);
26+
const innerBlocksRef = useRef();
27+
const isDraggingWithinInnerBlocks = useIsDraggingWithin( innerBlocksRef );
28+
const shouldHighlightDropZone = isDraggingWithinInnerBlocks;
29+
// Using the experimental hook so that we can control the className of the element.
30+
const innerBlocksProps = __experimentalUseInnerBlocksProps(
31+
{ ref: innerBlocksRef },
32+
{
33+
value: blocks,
34+
onInput,
35+
onChange,
36+
templateLock: false,
37+
renderAppender: InnerBlocks.ButtonBlockAppender,
38+
}
39+
);
40+
1241
return (
13-
<InnerBlocks
14-
value={ blocks }
15-
onInput={ onInput }
16-
onChange={ onChange }
17-
templateLock={ false }
18-
renderAppender={ InnerBlocks.ButtonBlockAppender }
19-
/>
42+
<div
43+
className={ classnames(
44+
'wp-block-widget-area__inner-blocks block-editor-inner-blocks editor-styles-wrapper',
45+
{
46+
'wp-block-widget-area__highlight-drop-zone': shouldHighlightDropZone,
47+
}
48+
) }
49+
>
50+
<div { ...innerBlocksProps } />
51+
</div>
2052
);
2153
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { useState, useEffect } from '@wordpress/element';
5+
6+
/** @typedef {import('@wordpress/element').RefObject} RefObject */
7+
8+
/**
9+
* A React hook to determine if it's dragging within the target element.
10+
*
11+
* @param {RefObject<HTMLElement>} elementRef The target elementRef object.
12+
*
13+
* @return {boolean} Is dragging within the target element.
14+
*/
15+
const useIsDraggingWithin = ( elementRef ) => {
16+
const [ isDraggingWithin, setIsDraggingWithin ] = useState( false );
17+
18+
useEffect( () => {
19+
const { ownerDocument } = elementRef.current;
20+
21+
function handleDragStart( event ) {
22+
// Check the first time when the dragging starts.
23+
handleDragEnter( event );
24+
}
25+
26+
// Set to false whenever the user cancel the drag event by either releasing the mouse or press Escape.
27+
function handleDragEnd() {
28+
setIsDraggingWithin( false );
29+
}
30+
31+
function handleDragEnter( event ) {
32+
// Check if the current target is inside the item element.
33+
if ( elementRef.current.contains( event.target ) ) {
34+
setIsDraggingWithin( true );
35+
} else {
36+
setIsDraggingWithin( false );
37+
}
38+
}
39+
40+
// Bind these events to the document to catch all drag events.
41+
// Ideally, we can also use `event.relatedTarget`, but sadly that doesn't work in Safari.
42+
ownerDocument.addEventListener( 'dragstart', handleDragStart );
43+
ownerDocument.addEventListener( 'dragend', handleDragEnd );
44+
ownerDocument.addEventListener( 'dragenter', handleDragEnter );
45+
46+
return () => {
47+
ownerDocument.removeEventListener( 'dragstart', handleDragStart );
48+
ownerDocument.removeEventListener( 'dragend', handleDragEnd );
49+
ownerDocument.removeEventListener( 'dragenter', handleDragEnter );
50+
};
51+
}, [] );
52+
53+
return isDraggingWithin;
54+
};
55+
56+
export default useIsDraggingWithin;
Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,72 @@
1+
$panel-title-height: 48px;
2+
13
.wp-block[data-type="core/widget-area"] {
24
max-width: $widget-area-width;
35
margin-left: auto;
46
margin-right: auto;
57

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

1023
// Remove default hover background in panel title. See #25752.
1124
&:hover {
12-
background: inherit;
25+
background: $white;
1326
}
1427
}
1528

16-
.components-panel__body .editor-styles-wrapper {
17-
margin: 0 (-$grid-unit-20) (-$grid-unit-20) (-$grid-unit-20);
18-
padding: $grid-unit-20;
19-
}
20-
2129
.block-list-appender.wp-block {
2230
width: initial;
2331
}
32+
2433
// Override theme custom widths for blocks.
2534
.editor-styles-wrapper .wp-block.wp-block.wp-block.wp-block.wp-block {
2635
max-width: 100%;
2736
}
37+
38+
.components-panel__body.is-opened {
39+
padding: 0;
40+
}
2841
}
2942

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

35-
// Ensure the widget area block lists have a minimum height so that it doesn't
36-
// collapse to zero height when it has no blocks. When that happens the block
37-
// can't be used as a drop target.
3847
> .block-editor-block-list__layout {
48+
// Stretch the inner-blocks container to cover the entire panel,
49+
// so that dragging onto anywhere in it works.
50+
margin-top: -$panel-title-height;
51+
// Add some spacing above the inner blocks so that the block toolbar doesn't
52+
// overlap the panel header.
53+
padding: ($panel-title-height + $grid-unit-30) $grid-unit-20 $grid-unit-20;
54+
55+
// Ensure the widget area block lists have a minimum height so that it doesn't
56+
// collapse to zero height when it has no blocks. When that happens the block
57+
// can't be used as a drop target.
3958
min-height: $grid-unit-40;
4059
}
4160
}
61+
62+
.wp-block-widget-area__highlight-drop-zone {
63+
outline: var(--wp-admin-border-width-focus) solid var(--wp-admin-theme-color);
64+
}
65+
66+
// Prevent "dragenter" event from firing when dragging onto the title component.
67+
body.is-dragging-components-draggable .wp-block[data-type="core/widget-area"] .components-panel__body > .components-panel__body-title {
68+
&,
69+
* {
70+
pointer-events: none;
71+
}
72+
}

0 commit comments

Comments
 (0)