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

Block editor: iframe: remove event bubbling and improve draggable chip #34819

Open
wants to merge 5 commits into
base: trunk
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions packages/base-styles/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ $z-layers: (

// The draggable element should show up above the entire UI
".components-draggable__clone": 1000000000,
".block-editor-block-draggable-chip-wrapper": 1000000000,
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be worth using a decimal literal for readability 1_000_000_000

Not sure how important this is, but it looks like the parent z-index stacking context for the chip wrapper is .interface-interface-skeleton__content. This means that despite the high z-index here items in sidebar and actions will be on top of the chip from natural stacking order.

CleanShot.2021-09-28.at.15.44.25.mp4

CleanShot 2021-09-28 at 15 43 55@2x


".block-editor-block-list__block .reusable-block-edit-panel *": 1,

Expand Down
5 changes: 3 additions & 2 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,9 @@ Undocumented declaration.
### BlockTools

Renders block tools (the block toolbar, select/navigation mode toolbar, the
insertion point and a slot for the inline rich text toolbar). Must be wrapped
around the block content and editor styles wrapper or iframe.
insertion point, a slot for the inline rich text toolbar, and the drag chip).
Also handles block-level keyboard shortcut handling. Must be wrapped around
the block content and editor styles wrapper or iframe.

_Parameters_

Expand Down
17 changes: 4 additions & 13 deletions packages/block-editor/src/components/block-draggable/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
/**
* WordPress dependencies
*/
import { getBlockType } from '@wordpress/blocks';
import { Draggable } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import BlockDraggableChip from './draggable-chip';
import useScrollWhenDragging from './use-scroll-when-dragging';
import { store as blockEditorStore } from '../../store';

Expand All @@ -20,23 +18,19 @@ const BlockDraggable = ( {
onDragStart,
onDragEnd,
} ) => {
const { srcRootClientId, isDraggable, icon } = useSelect(
const { srcRootClientId, isDraggable } = useSelect(
( select ) => {
const {
getBlockRootClientId,
getTemplateLock,
getBlockName,
} = select( blockEditorStore );
const { getBlockRootClientId, getTemplateLock } = select(
blockEditorStore
);
const rootClientId = getBlockRootClientId( clientIds[ 0 ] );
const templateLock = rootClientId
? getTemplateLock( rootClientId )
: null;
const blockName = getBlockName( clientIds[ 0 ] );

return {
srcRootClientId: rootClientId,
isDraggable: 'all' !== templateLock,
icon: getBlockType( blockName )?.icon,
};
},
[ clientIds ]
Expand Down Expand Up @@ -97,9 +91,6 @@ const BlockDraggable = ( {
onDragEnd();
}
} }
__experimentalDragComponent={
<BlockDraggableChip count={ clientIds.length } icon={ icon } />
}
>
{ ( { onDraggableStart, onDraggableEnd } ) => {
return children( {
Expand Down
47 changes: 0 additions & 47 deletions packages/block-editor/src/components/block-draggable/style.scss
Original file line number Diff line number Diff line change
@@ -1,50 +1,3 @@
.block-editor-block-draggable-chip-wrapper {
position: absolute;
// Offset position so that cursor is centered over the drag handle.
top: -#{$block-toolbar-height * 0.5};
left: 0;
}

.block-editor-block-draggable-chip {
background-color: $gray-900;
border-radius: $radius-block-ui;
box-shadow: 0 6px 8px rgba($black, 0.3);
color: $white;
cursor: grabbing;
display: inline-flex;
height: $block-toolbar-height;
padding: 0 ( $grid-unit-15 + $border-width );
user-select: none;

svg {
fill: currentColor;
}

.block-editor-block-draggable-chip__content {
margin: auto;
justify-content: flex-start;

> .components-flex__item {
margin-right: $grid-unit-15 * 0.5;

&:last-child {
margin-right: 0;
}
}

// Drag handle is smaller than the others.
.block-editor-block-icon svg {
min-width: 18px;
min-height: 18px;
}
}

.components-flex__item {
font-family: $default-font;
font-size: $default-font-size;
}
}

// This hides the block being dragged.
// The effect is that the block being dragged appears to be "lifted".
.is-dragging {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
/**
* WordPress dependencies
*/
import { _n, sprintf } from '@wordpress/i18n';
import { Flex, FlexItem } from '@wordpress/components';
import { dragHandle } from '@wordpress/icons';
import { forwardRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import BlockIcon from '../block-icon';

export default function BlockDraggableChip( { count, icon } ) {
function BlockDraggableChip( { icon }, ref ) {
return (
<div className="block-editor-block-draggable-chip-wrapper">
<div className="block-editor-block-draggable-chip-wrapper" ref={ ref }>
<div className="block-editor-block-draggable-chip">
<Flex
justify="center"
className="block-editor-block-draggable-chip__content"
>
<FlexItem>
{ icon ? (
<BlockIcon icon={ icon } />
) : (
sprintf(
/* translators: %d: Number of blocks. */
_n( '%d block', '%d blocks', count ),
count
)
) }
<BlockIcon icon={ icon } />
</FlexItem>
<FlexItem>
<BlockIcon icon={ dragHandle } />
Expand All @@ -37,3 +29,5 @@ export default function BlockDraggableChip( { count, icon } ) {
</div>
);
}

export default forwardRef( BlockDraggableChip );
120 changes: 113 additions & 7 deletions packages/block-editor/src/components/block-tools/index.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
/**
* External dependencies
*/
import { first, last } from 'lodash';
import { first, last, uniq } from 'lodash';

/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useViewportMatch } from '@wordpress/compose';
import { useViewportMatch, useRefEffect } from '@wordpress/compose';
import { Popover } from '@wordpress/components';
import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts';
import { useState, useRef } from '@wordpress/element';
import { getBlockType } from '@wordpress/blocks';
import { stack } from '@wordpress/icons';

/**
* Internal dependencies
*/
import InsertionPoint from './insertion-point';
import BlockPopover from './block-popover';
import BlockDraggableChip from './draggable-chip';
import { store as blockEditorStore } from '../../store';
import BlockContextualToolbar from './block-contextual-toolbar';
import { usePopoverScroll } from './use-popover-scroll';
import { parseDropEvent } from '../use-on-block-drop';

/**
* Renders block tools (the block toolbar, select/navigation mode toolbar, the
* insertion point and a slot for the inline rich text toolbar). Must be wrapped
* around the block content and editor styles wrapper or iframe.
* insertion point, a slot for the inline rich text toolbar, and the drag chip).
* Also handles block-level keyboard shortcut handling. Must be wrapped around
* the block content and editor styles wrapper or iframe.
*
* @param {Object} $0 Props.
* @param {Object} $0.children The block content and style container.
* @param {Object} $0.__unstableContentRef Ref holding the content scroll container.
* @param {Object} $0.children The block content and style
* container.
* @param {Object} $0.__unstableContentRef Ref holding the content scroll
* container.
*/
export default function BlockTools( {
children,
Expand Down Expand Up @@ -112,9 +120,101 @@ export default function BlockTools( {
}
}

const [ dragIcon, setDragIcon ] = useState();
const draggableChipRef = useRef();
const { getBlockName } = useSelect( blockEditorStore );

function onIframeDragOver( event ) {
// Abort if we're not dragging over an iframe. The global event listener
// will handle the positioning of the drag chip.
if ( event.currentTarget.contains( event.target ) ) return;

const { ownerDocument } = event.target;
const { defaultView } = ownerDocument;
const { frameElement } = defaultView;
const rect = frameElement.getBoundingClientRect();
const element = draggableChipRef.current;

element.style.left = event.clientX + rect.left + 'px';
element.style.top = event.clientY + rect.top + 'px';
}

function onIframeDragEnd( event ) {
// Abort if we're not dragging over an iframe. The global event listener
// will handle resetting the dragged state.
if ( event.currentTarget.contains( event.target ) ) return;

setDragIcon();
}

const refEffect = useRefEffect(
( element ) => {
const { ownerDocument } = element;

if ( dragIcon ) {
function onDragOver( event ) {
const chip = draggableChipRef.current;
chip.style.left = event.clientX + 'px';
chip.style.top = event.clientY + 'px';
}

function onDragEnd() {
setDragIcon();
}

ownerDocument.addEventListener( 'dragover', onDragOver );
ownerDocument.addEventListener( 'dragend', onDragEnd );
return () => {
ownerDocument.removeEventListener( 'dragover', onDragOver );
ownerDocument.removeEventListener( 'dragend', onDragEnd );
};
}

function onDragStart( event ) {
const { srcClientIds, blocks } = parseDropEvent( event );
let names = [];

if ( srcClientIds ) {
names = srcClientIds.map( getBlockName );
} else if ( blocks ) {
names = blocks.map( ( { name } ) => name );
}

if ( ! names.length ) {
return;
}

// When selection consists of blocks of multiple types, display an
// appropriate icon to communicate the non-uniformity.
setDragIcon(
uniq( names ).length === 1
? getBlockType( first( names ) )?.icon
: stack
);
}

ownerDocument.addEventListener( 'dragstart', onDragStart );
return () => {
ownerDocument.removeEventListener( 'dragstart', onDragStart );
};
},
[ dragIcon ]
);

return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div { ...props } onKeyDown={ onKeyDown }>
<div
{ ...props }
onKeyDown={ onKeyDown }
// React event listeners to listen inside the iframe (the events
// bubble through a React portal).
onDragOver={ dragIcon ? onIframeDragOver : null }
onDragEnd={ dragIcon ? onIframeDragEnd : null }
// Global event listeners to track drag events on the entire page:
// drag may start somewhere else on the page and may also be dragged
// outside these boundaries.
ref={ refEffect }
>
<InsertionPoint __unstableContentRef={ __unstableContentRef }>
{ ( hasFixedToolbar || ! isLargeViewport ) && (
<BlockContextualToolbar isFixed />
Expand All @@ -128,6 +228,12 @@ export default function BlockTools( {
ref={ usePopoverScroll( __unstableContentRef ) }
/>
{ children }
{ dragIcon && (
<BlockDraggableChip
icon={ dragIcon }
ref={ draggableChipRef }
/>
) }
{ /* Forward compatibility: a place to render block tools behind the
content so it can be tabbed to properly. */ }
</InsertionPoint>
Expand Down
49 changes: 49 additions & 0 deletions packages/block-editor/src/components/block-tools/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,52 @@
.is-dragging-components-draggable .components-tooltip {
display: none;
}

.block-editor-block-draggable-chip-wrapper {
position: fixed;
// Offset position so that cursor is centered over the drag handle.
transform: translate(-#{$block-toolbar-height * 0.5}, -#{$block-toolbar-height * 0.5});
left: 0;
pointer-events: none;
z-index: z-index(".block-editor-block-draggable-chip-wrapper");
}

.block-editor-block-draggable-chip {
background-color: $gray-900;
border-radius: $radius-block-ui;
box-shadow: 0 6px 8px rgba($black, 0.3);
color: $white;
cursor: grabbing;
display: inline-flex;
height: $block-toolbar-height;
padding: 0 ( $grid-unit-15 + $border-width );
user-select: none;

svg {
fill: currentColor;
}

.block-editor-block-draggable-chip__content {
margin: auto;
justify-content: flex-start;

> .components-flex__item {
margin-right: $grid-unit-15 * 0.5;

&:last-child {
margin-right: 0;
}
}

// Drag handle is smaller than the others.
.block-editor-block-icon svg {
min-width: 18px;
min-height: 18px;
}
}

.components-flex__item {
font-family: $default-font;
font-size: $default-font-size;
}
}
Loading