diff --git a/edit-post/assets/stylesheets/_z-index.scss b/edit-post/assets/stylesheets/_z-index.scss
index 48ae17cbffa329..9f44e1dcb1564e 100644
--- a/edit-post/assets/stylesheets/_z-index.scss
+++ b/edit-post/assets/stylesheets/_z-index.scss
@@ -33,9 +33,6 @@ $z-layers: (
// Active pill button
".components-button.is-button {:focus or .is-primary}": 1,
- // Should have lower index than anything else positioned inside the block containers
- ".editor-block-list__block-draggable": 0,
-
// The draggable element should show up above the entire UI
".components-draggable__clone": 1000000000,
diff --git a/packages/editor/src/components/block-draggable/index.js b/packages/editor/src/components/block-draggable/index.js
new file mode 100644
index 00000000000000..7b5ec2ac74912b
--- /dev/null
+++ b/packages/editor/src/components/block-draggable/index.js
@@ -0,0 +1,41 @@
+/**
+ * WordPress dependencies
+ */
+import { Draggable } from '@wordpress/components';
+import { withSelect } from '@wordpress/data';
+
+const BlockDraggable = ( { children, clientId, rootClientId, blockElementId, index, layout, onDragStart, onDragEnd } ) => {
+ const transferData = {
+ type: 'block',
+ fromIndex: index,
+ rootClientId,
+ clientId,
+ layout,
+ };
+
+ return (
+
+ {
+ ( { onDraggableStart, onDraggableEnd } ) => {
+ return children( {
+ onDraggableStart: onDraggableStart,
+ onDraggableEnd: onDraggableEnd,
+ } );
+ }
+ }
+
+ );
+};
+
+export default withSelect( ( select, { clientId } ) => {
+ const { getBlockIndex, getBlockRootClientId } = select( 'core/editor' );
+ return {
+ index: getBlockIndex( clientId ),
+ rootClientId: getBlockRootClientId( clientId ),
+ };
+} )( BlockDraggable );
diff --git a/packages/editor/src/components/block-list/block-draggable.js b/packages/editor/src/components/block-list/block-draggable.js
deleted file mode 100644
index 0c8caa7410c081..00000000000000
--- a/packages/editor/src/components/block-list/block-draggable.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-
-/**
- * WordPress dependencies
- */
-import { Draggable } from '@wordpress/components';
-
-const BlockDraggable = ( { clientId, rootClientId, blockElementId, layout, order, isDragging, onDragStart, onDragEnd } ) => {
- const className = classnames( 'editor-block-list__block-draggable', {
- 'is-visible': isDragging,
- } );
-
- const transferData = {
- type: 'block',
- fromIndex: order,
- rootClientId,
- clientId,
- layout,
- };
-
- return (
-
- {
- ( { onDraggableStart, onDraggableEnd } ) => (
- )
- }
-
- );
-};
-
-export default BlockDraggable;
diff --git a/packages/editor/src/components/block-list/block.js b/packages/editor/src/components/block-list/block.js
index 2ff6c3dc272cf8..e417996f4e01a7 100644
--- a/packages/editor/src/components/block-list/block.js
+++ b/packages/editor/src/components/block-list/block.js
@@ -43,7 +43,6 @@ import BlockContextualToolbar from './block-contextual-toolbar';
import BlockMultiControls from './multi-controls';
import BlockMobileToolbar from './block-mobile-toolbar';
import BlockInsertionPoint from './insertion-point';
-import BlockDraggable from './block-draggable';
import IgnoreNestedEvents from './ignore-nested-events';
import InserterWithShortcuts from '../inserter-with-shortcuts';
import Inserter from '../inserter';
@@ -373,6 +372,7 @@ export class BlockListBlock extends Component {
hasSelectedInnerBlock,
isParentOfSelectedBlock,
hasMultiSelection,
+ isDraggable,
} = this.props;
const isHovered = this.state.isHovered && ! isMultiSelecting;
const { name: blockName, isValid } = block;
@@ -410,7 +410,7 @@ export class BlockListBlock extends Component {
'is-selected-parent': shouldAppearSelectedParent,
'is-hovered': shouldAppearHovered,
'is-reusable': isReusableBlock( blockType ),
- 'is-hidden': dragging,
+ 'is-dragging': dragging,
'is-typing': isTypingWithinBlock,
'is-focused': isFocusMode && ( isSelected || isParentOfSelectedBlock ),
'is-focus-mode': isFocusMode,
@@ -480,18 +480,6 @@ export class BlockListBlock extends Component {
] }
{ ...wrapperProps }
>
- { ! isPartOfMultiSelection && isMovable && (
-
- ) }
{ shouldShowInsertionPoint && (
) }
{ shouldShowBreadcrumb && (
diff --git a/packages/editor/src/components/block-list/index.js b/packages/editor/src/components/block-list/index.js
index a1f69889e0c00b..5164e9e5a808ec 100644
--- a/packages/editor/src/components/block-list/index.js
+++ b/packages/editor/src/components/block-list/index.js
@@ -196,6 +196,7 @@ class BlockList extends Component {
isGroupedByLayout,
rootClientId,
canInsertDefaultBlock,
+ isDraggable,
} = this.props;
let defaultLayout;
@@ -221,6 +222,7 @@ class BlockList extends Component {
layout={ defaultLayout }
isFirst={ blockIndex === 0 }
isLast={ blockIndex === blockClientIds.length - 1 }
+ isDraggable={ isDraggable }
/>
) ) }
{ canInsertDefaultBlock && (
diff --git a/packages/editor/src/components/block-list/style.scss b/packages/editor/src/components/block-list/style.scss
index f8ba61e2074e69..6afce0fcbc3858 100644
--- a/packages/editor/src/components/block-list/style.scss
+++ b/packages/editor/src/components/block-list/style.scss
@@ -1,104 +1,35 @@
.editor-block-list__layout .components-draggable__clone {
- & > .editor-block-list__block > .editor-block-list__block-draggable {
- background: $white; // @todo: ensure this works with themes that invert the color
- box-shadow: $shadow-popover;
-
- @include break-small {
- left: -$block-parent-side-ui-clearance - $border-width;
- right: -$block-parent-side-ui-clearance - $border-width;
-
- .editor-block-list__layout & {
- left: -$border-width;
- right: -$border-width;
- }
- }
- }
-
// Hide the Block UI when dragging the block
// This ensures the page scroll properly (no sticky elements)
.editor-block-contextual-toolbar,
- .editor-block-mover,
.editor-block-settings-menu {
// I think important is fine here to avoid over complexing the selector
display: none !important;
}
}
-.editor-block-list__layout .editor-block-list__block-draggable {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: z-index(".editor-block-list__block-draggable");
-
- > .editor-block-list__block-draggable-inner {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- visibility: hidden;
-
- // Use opacity to work in various editor styles.
- background-color: $dark-opacity-light-200;
-
- .is-dark-theme & {
- background-color: $light-opacity-light-200;
+.editor-block-list__layout .editor-block-list__block.is-selected { // Needs specificity to override inherited styles.
+ // While block is being dragged, dim the slot dragged from, and hide some UI.
+ &.is-dragging {
+ .editor-block-list__block-edit::before {
+ outline: none;
}
- @include break-small {
- margin: 0 48px;
+ > .editor-block-list__block-edit > * {
+ background: $light-gray-100;
}
- }
-
- &.is-visible > .editor-block-list__block-draggable-inner {
- visibility: visible;
- }
- @include break-small {
- // use a wider available space for hovering/selecting/dragging on top level blocks
- left: -$parent-block-padding - $block-padding;
- right: -$parent-block-padding - $block-padding;
-
- // use smaller space for hovering/selecting/dragging on child blocks
- .editor-block-list__layout & {
- left: 0;
- right: 0;
+ > .editor-block-list__block-edit > * > * {
+ visibility: hidden;
}
- // Full width blocks don't have the place to expand on the side
- .editor-block-list__block[data-align="full"] & {
- left: 0;
- right: 0;
+ .editor-block-mover,
+ .editor-block-contextual-toolbar {
+ display: none;
}
}
-
- cursor: move; // Fallback for IE/Edge < 14
- cursor: grab;
-}
-
-
-// Allow Drag & Drop when clicking on the empty area of the mover and the settings menu
-.editor-block-list__layout .editor-block-list__block .editor-block-mover,
-.editor-block-list__layout .editor-block-list__block .editor-block-settings-menu {
- pointer-events: none;
-
- // Nested blocks don't have any side affordance for drag and drop
- .editor-block-list__layout &,
- > * {
- pointer-events: auto;
- }
-}
-
-.editor-block-list__block {
- &.is-hidden *,
- &.is-hidden > * {
- visibility: hidden;
- }
-
- .editor-block-list__block-edit .reusable-block-edit-panel * {
+ > .editor-block-list__block-edit .reusable-block-edit-panel * {
z-index: z-index(".editor-block-list__block-edit .reusable-block-edit-panel *");
}
@@ -438,12 +369,6 @@
float: left;
}
- // There is no side UI clearance on full-wide elements, so they are simply not draggable on the sides
- > .editor-block-list__block-draggable {
- left: 0;
- right: 0;
- }
-
// Position hover label on the right
> .editor-block-list__breadcrumb {
right: -$border-width;
diff --git a/packages/editor/src/components/block-mover/drag-handle.js b/packages/editor/src/components/block-mover/drag-handle.js
new file mode 100644
index 00000000000000..9597a6ef049743
--- /dev/null
+++ b/packages/editor/src/components/block-mover/drag-handle.js
@@ -0,0 +1,40 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import BlockDraggable from '../block-draggable';
+
+export const IconDragHandle = ( { isVisible, className, icon, onDragStart, onDragEnd, blockElementId, clientId, layout } ) => {
+ if ( ! isVisible ) {
+ return null;
+ }
+
+ const dragHandleClassNames = classnames( 'editor-block-mover__control-drag-handle', className );
+
+ return (
+
+ {
+ ( { onDraggableStart, onDraggableEnd } ) => (
+
+ { icon }
+
+ ) }
+
+ );
+};
diff --git a/packages/editor/src/components/block-mover/arrows.js b/packages/editor/src/components/block-mover/icons.js
similarity index 51%
rename from packages/editor/src/components/block-mover/arrows.js
rename to packages/editor/src/components/block-mover/icons.js
index 8b4438bce0ac18..d23ec6cc153897 100644
--- a/packages/editor/src/components/block-mover/arrows.js
+++ b/packages/editor/src/components/block-mover/icons.js
@@ -14,3 +14,11 @@ export const downArrow = (
);
+
+export const dragHandle = (
+
+
+
+);
diff --git a/packages/editor/src/components/block-mover/index.js b/packages/editor/src/components/block-mover/index.js
index d28d131cbdfc6c..b431c71c3e7c9f 100644
--- a/packages/editor/src/components/block-mover/index.js
+++ b/packages/editor/src/components/block-mover/index.js
@@ -18,7 +18,8 @@ import { withInstanceId, compose } from '@wordpress/compose';
* Internal dependencies
*/
import { getBlockMoverDescription } from './mover-description';
-import { upArrow, downArrow } from './arrows';
+import { upArrow, downArrow, dragHandle } from './icons';
+import { IconDragHandle } from './drag-handle';
export class BlockMover extends Component {
constructor() {
@@ -43,7 +44,7 @@ export class BlockMover extends Component {
}
render() {
- const { onMoveUp, onMoveDown, isFirst, isLast, clientIds, blockType, firstIndex, isLocked, instanceId, isHidden } = this.props;
+ const { onMoveUp, onMoveDown, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, layout, blockType, firstIndex, isLocked, instanceId, isHidden } = this.props;
const { isFocused } = this.state;
const blocksCount = castArray( clientIds ).length;
if ( isLocked || ( isFirst && isLast ) ) {
@@ -66,6 +67,16 @@ export class BlockMover extends Component {
onFocus={ this.onFocus }
onBlur={ this.onBlur }
/>
+
{
- const { getBlock, getBlockIndex, getTemplateLock } = select( 'core/editor' );
+ withSelect( ( select, { clientIds } ) => {
+ const { getBlock, getBlockIndex, getTemplateLock, getBlockRootClientId } = select( 'core/editor' );
const firstClientId = first( castArray( clientIds ) );
const block = getBlock( firstClientId );
+ const rootClientId = getBlockRootClientId( first( castArray( clientIds ) ) );
return {
firstIndex: getBlockIndex( firstClientId, rootClientId ),
blockType: block ? getBlockType( block.name ) : null,
isLocked: getTemplateLock( rootClientId ) === 'all',
+ rootClientId,
};
} ),
withDispatch( ( dispatch, { clientIds, rootClientId } ) => {
diff --git a/packages/editor/src/components/block-mover/style.scss b/packages/editor/src/components/block-mover/style.scss
index c9bb11ba20298a..9c9b8148202592 100644
--- a/packages/editor/src/components/block-mover/style.scss
+++ b/packages/editor/src/components/block-mover/style.scss
@@ -6,9 +6,19 @@
&.is-visible {
@include fade_in;
}
+
+ // 24px is the smallest size of a good pressable button.
+ // With 3 pieces of side UI, that comes to a total of 72px.
+ // To vertically center against a 56px paragraph, move upwards 72px - 56px / 2.
+ // Don't do this for wide, fullwide, or mobile.
+ @include break-small() {
+ .editor-block-list__block:not([data-align="wide"]):not([data-align="full"]) & {
+ margin-top: -8px;
+ }
+ }
}
-// Mover icon buttons
+// Mover icon buttons.
.editor-block-mover__control {
display: flex;
align-items: center;
@@ -18,11 +28,19 @@
background: none;
cursor: pointer;
padding: 0;
- width: $block-side-ui-width;
- height: $block-side-ui-width; // the side UI can be no taller than 2 * $block-side-ui-width, which matches the height of a line of text
border-radius: $radius-round-rectangle;
- // use opacity to work in various editor styles
+ // Carefully adjust the size of the side UI to fit one paragraph of text (56px).
+ width: $block-side-ui-width;
+ height: $icon-button-size-small;
+
+ svg {
+ width: $block-side-ui-width;
+ height: $icon-button-size-small;
+ padding: #{ ($block-side-ui-width - $icon-button-size-small) / 2 } #{ ($block-side-ui-width - 18px) / 2 }; // This makes the SVG fill the whole available area, without scaling the artwork.
+ }
+
+ // Use opacity to work in various editor styles
color: $dark-opacity-300;
.is-dark-theme & {
@@ -32,20 +50,14 @@
&[aria-disabled="true"] {
cursor: default;
pointer-events: none;
- color: $dark-opacity-light-300; // use opacity to work in various editor styles
+ color: $dark-opacity-light-300; // Use opacity to work in various editor styles.
.is-dark-theme & {
color: $light-opacity-light-300;
}
}
- svg {
- width: $block-side-ui-width;
- height: $block-side-ui-width;
- padding: ($block-side-ui-width - 18px) / 2; // this makes the SVG fill the whole available area, without scaling the artwork
- }
-
- // Apply a background in nested contexts, only on desktop
+ // Apply a background in nested contexts, only on desktop.
@include break-small() {
.editor-block-list__layout .editor-block-list__layout & {
background: $white;
@@ -78,6 +90,30 @@
}
}
+.editor-block-mover__control-drag-handle {
+ cursor: move; // Fallback for IE/Edge < 14
+ cursor: grab;
+
+ &,
+ &:not(:disabled):not([aria-disabled="true"]):not(.is-default):hover,
+ &:not(:disabled):not([aria-disabled="true"]):not(.is-default):active,
+ &:not(:disabled):not([aria-disabled="true"]):not(.is-default):focus {
+ box-shadow: none;
+ background: none;
+
+ // Use opacity to work in various editor styles.
+ color: $dark-opacity-500;
+
+ .is-dark-theme & {
+ color: $light-opacity-500;
+ }
+ }
+
+ &:not(:disabled):not([aria-disabled="true"]):not(.is-default):active {
+ cursor: grabbing;
+ }
+}
+
.editor-block-mover__description {
display: none;
}
diff --git a/packages/editor/src/components/block-mover/test/index.js b/packages/editor/src/components/block-mover/test/index.js
index e22973b71f78f8..ea13280f4d3117 100644
--- a/packages/editor/src/components/block-mover/test/index.js
+++ b/packages/editor/src/components/block-mover/test/index.js
@@ -7,7 +7,7 @@ import { shallow } from 'enzyme';
* Internal dependencies
*/
import { BlockMover } from '../';
-import { upArrow, downArrow } from '../arrows';
+import { upArrow, downArrow, dragHandle } from '../icons';
describe( 'BlockMover', () => {
describe( 'basic rendering', () => {
@@ -22,7 +22,7 @@ describe( 'BlockMover', () => {
expect( wrapper.type() ).toBe( null );
} );
- it( 'should render two IconButton components with the following props', () => {
+ it( 'should render three icons with the following props', () => {
const blockMover = shallow(
{
expect( blockMover.hasClass( 'editor-block-mover' ) ).toBe( true );
const moveUp = blockMover.childAt( 0 );
- const moveDown = blockMover.childAt( 1 );
- const moveUpDesc = blockMover.childAt( 2 );
- const moveDownDesc = blockMover.childAt( 3 );
+ const drag = blockMover.childAt( 1 );
+ const moveDown = blockMover.childAt( 2 );
+ const moveUpDesc = blockMover.childAt( 3 );
+ const moveDownDesc = blockMover.childAt( 4 );
expect( moveUp.type().name ).toBe( 'IconButton' );
+ expect( drag.type().name ).toBe( 'IconDragHandle' );
expect( moveDown.type().name ).toBe( 'IconButton' );
expect( moveUp.props() ).toMatchObject( {
className: 'editor-block-mover__control',
@@ -46,6 +48,10 @@ describe( 'BlockMover', () => {
'aria-disabled': undefined,
'aria-describedby': 'editor-block-mover__up-description-1',
} );
+ expect( drag.props() ).toMatchObject( {
+ className: 'editor-block-mover__control',
+ icon: dragHandle,
+ } );
expect( moveDown.props() ).toMatchObject( {
className: 'editor-block-mover__control',
onClick: undefined,
@@ -72,6 +78,23 @@ describe( 'BlockMover', () => {
expect( moveUp.prop( 'onClick' ) ).toBe( onMoveUp );
} );
+ it( 'should render the drag handle with onDragStart and onDragEnd callback', () => {
+ const onDragStart = ( event ) => event;
+ const onDragEnd = ( event ) => event;
+ const blockMover = shallow(
+
+ );
+ const dragHandler = blockMover.childAt( 1 );
+ expect( dragHandler.prop( 'onDragStart' ) ).toBe( onDragStart );
+ expect( dragHandler.prop( 'onDragEnd' ) ).toBe( onDragEnd );
+ } );
+
it( 'should render the down arrow with a onMoveDown callback', () => {
const onMoveDown = ( event ) => event;
const blockMover = shallow(
@@ -82,26 +105,21 @@ describe( 'BlockMover', () => {
firstIndex={ 0 }
/>
);
- const moveDown = blockMover.childAt( 1 );
+ const moveDown = blockMover.childAt( 2 );
expect( moveDown.prop( 'onClick' ) ).toBe( onMoveDown );
} );
- it( 'should render with a disabled up arrow when the block isFirst', () => {
- const onMoveUp = ( event ) => event;
+ it( 'should not render the drag handle if block is not draggable', () => {
const blockMover = shallow(
);
- const moveUp = blockMover.childAt( 0 );
- expect( moveUp.props() ).toMatchObject( {
- onClick: null,
- 'aria-disabled': true,
- } );
+ const dragHandler = blockMover.childAt( 1 );
+ expect( dragHandler.type().name ).toBe( 'IconDragHandle' );
+ expect( dragHandler.prop( 'isVisible' ) ).toBe( false );
} );
it( 'should render with a disabled down arrow when the block isLast', () => {
@@ -115,7 +133,7 @@ describe( 'BlockMover', () => {
firstIndex={ 0 }
/>
);
- const moveDown = blockMover.childAt( 1 );
+ const moveDown = blockMover.childAt( 2 );
expect( moveDown.props() ).toMatchObject( {
onClick: null,
'aria-disabled': true,