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

Reorder blocks via drag & drop (v2. using editor dropzones). #4115

Merged
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
b62160a
Drag & Drop: Added functionality for reindexing blocks.
chriskmnds Dec 20, 2017
7723d70
Drag & Drop for blocks: Added drag listeners and styling for dragging…
chriskmnds Dec 20, 2017
51cd0f5
Drag & Drop for blocks: Updated drop logic to improve precision of th…
chriskmnds Dec 21, 2017
82e9886
Drag & Drop for blocks: Resolved conflicts with latest master.
chriskmnds Dec 22, 2017
d6df73d
Drag & Drop for blocks: Added z-index to block underlay used in dragg…
chriskmnds Dec 27, 2017
f38db58
Drag & Drop for blocks: Changed background color of block inset/underlay
chriskmnds Dec 27, 2017
1b04b5b
Drag & Drop for blocks: Fixed visible grayed inset area when dragging.
chriskmnds Dec 27, 2017
9f25b10
Drag & Drop for blocks: Removed unnecessary block container element.
chriskmnds Dec 27, 2017
0a23609
Drag & Drop for blocks: Updated cursor when hovering/dragging a
chriskmnds Dec 27, 2017
9f3b3af
Drag & Drop for blocks: Added z-index property to placeholder compone…
chriskmnds Dec 28, 2017
52ac1ff
Drag & Drop for blocks: Updated dragging inset margin to be effective…
chriskmnds Dec 28, 2017
c55ebc3
Merge branch 'master' into add/drag-n-drop-for-blocks--dropzone
chriskmnds Dec 29, 2017
6c7a913
Drag & Drop for blocks: Fixed issues that surfaced since merging late…
chriskmnds Dec 30, 2017
8dbc69e
Drag & Drop for blocks: Some cleanup - renamed 'underlay' to 'drag-in…
chriskmnds Dec 30, 2017
35f9923
Drag & Drop for blocks: Changed dataTransfer data type for moving blo…
chriskmnds Dec 30, 2017
9271649
Drag & Drop for blocks: Improved conditioning for onDrop handler.
chriskmnds Jan 4, 2018
8fdcd6e
Drag & Drop for blocks: Added a small margin between cursor and drag
chriskmnds Jan 4, 2018
1e2ae5e
Drag & Drop for blocks: Removed browser prefix for cursor style.
chriskmnds Jan 4, 2018
ec5052c
Drag & Drop for blocks: IE11 tweaks. Added conditional call to
chriskmnds Jan 4, 2018
62afc6d
Drag & Drop for blocks: Some styling cleanup.
chriskmnds Jan 5, 2018
8f13212
Drag & Drop for blocks: IE11/Safari tweaks.
chriskmnds Jan 6, 2018
883ab19
Drag & Drop for blocks: Updated logic for setting drag image.
chriskmnds Jan 7, 2018
6faceef
Drag & Drop for blocks: Updated drag image logic to be consistent across
chriskmnds Jan 9, 2018
4978348
Drag & Drop for blocks: Updated drag image shadows and styling.
chriskmnds Jan 13, 2018
b512271
Drag & Drop for blocks: Updated logic for controlling the visibility of
chriskmnds Jan 14, 2018
e2c2e61
Drag & Drop for blocks: Some cleanup after code review.
chriskmnds Jan 16, 2018
8436f21
Drag & Drop for blocks: Added more draggable handles to the block, in…
chriskmnds Jan 17, 2018
f131740
Drag & Drop for blocks: Some cleanup.
chriskmnds Jan 17, 2018
2599d42
Drag & Drop for blocks: Updated cursor to move/grab for the block-set…
chriskmnds Jan 18, 2018
6ad1c44
Drag & Drop for blocks: Cleanup from previous commit - set the cursor…
chriskmnds Jan 18, 2018
6fc98de
Drag & Drop for blocks: Updated dragging logic to move clone around i…
chriskmnds Jan 19, 2018
178cc60
Drag & Drop for blocks: Some cleanup - removed logging, and added a h…
chriskmnds Jan 19, 2018
c98ea9c
Drag & Drop for blocks: Cleared linting errors.
chriskmnds Jan 19, 2018
6c2bf61
Drag & Drop for blocks: Updated drag start/end handlers to stop propa…
chriskmnds Jan 21, 2018
ab68b11
Drag & Drop for blocks: Added a fake/invisible drag image to avoid
chriskmnds Jan 21, 2018
702eec3
Drag & Drop for blocks: Added transformation to the block clone if
chriskmnds Jan 22, 2018
7cbdfde
Drag & Drop for blocks: Some code cleanup.
chriskmnds Jan 22, 2018
b0f0b0e
Drag & Drop for blocks: Updated block-mover and dropdown components to
chriskmnds Jan 23, 2018
43ec29a
Drag & Drop for blocks: Some cleanup - inline comments, new lines, etc.
chriskmnds Jan 28, 2018
ec53f4f
Drag & Drop for blocks: Making sure the DOM is not updated prior to r…
chriskmnds Feb 9, 2018
2e0b13a
Drag & Drop for blocks: Moved drag init/end logic to a higher order
chriskmnds Feb 12, 2018
a2246f4
Drag & Drop for blocks: Cleanup.
chriskmnds Feb 12, 2018
db7a34b
Drag & Drop for blocks: Resolved conflicts with master. Several updat…
chriskmnds Feb 12, 2018
d8bedee
Drag & Drop for blocks: Cleanup. Removed leftover styles.
chriskmnds Feb 12, 2018
25485f9
Drag & Drop for blocks: Code cleanup.
chriskmnds Feb 13, 2018
05f2d10
Drag & Drop for blocks: Code cleanup. Further extracted drag init/end
chriskmnds Feb 13, 2018
f33d4db
Drag & Drop for blocks: Resolved conflicts.
chriskmnds Feb 13, 2018
a83c738
Drag & Drop for blocks: Updated cursor styling to grab for the new bl…
chriskmnds Feb 13, 2018
30ee308
Drag & Drop for blocks: Updates after PR feedback.
chriskmnds Feb 24, 2018
da001c9
Drag & Drop for blocks: Some cleanup after PR feedback. removed unnec…
chriskmnds Feb 24, 2018
b071cc4
Drag & Drop for blocks: Updated cursor styling after PR feedback to g…
chriskmnds Feb 24, 2018
46ed7a2
Drag & Drop for blocks: Updated index of reusable block edit panel to…
chriskmnds Feb 25, 2018
129dac9
Drag & Drop for blocks: Some cleanup after PR feedback. Renamed _stat…
chriskmnds Feb 25, 2018
d6897ce
Drag & Drop for blocks: Added support for inner blocks.
chriskmnds Mar 4, 2018
4e66a4a
Drag & Drop for blocks: Cleanup - lint errors.
chriskmnds Mar 4, 2018
bfb78c1
Drag & Drop for blocks: Updates after PR review. No longer passing drag
chriskmnds Mar 10, 2018
0a6a05d
Drag & Drop for blocks: Got rid of BLOCK_REORDER constant and selector.
chriskmnds Mar 10, 2018
f72e05d
Drag & Drop for blocks: Updates and cleanup after PR review.
chriskmnds Mar 10, 2018
f03e4ec
Drag & Drop for blocks: Cleanup. Removed commented out code.
chriskmnds Mar 10, 2018
650f613
Merge remote-tracking branch 'origin/master' into add/drag-n-drop-for…
youknowriad Mar 23, 2018
5c1d339
Drag And Drop: Refactor using a component instead of Higher Order Com…
youknowriad Mar 23, 2018
5c1613d
Clarify Drop Events
youknowriad Mar 23, 2018
4d3b8b8
Fix dragging between nested and not nested blocks
youknowriad Mar 23, 2018
d731edf
Fix Drag Area and Styling
youknowriad Mar 23, 2018
9624dca
More cleaning and fix insert position
youknowriad Mar 23, 2018
94dd1cc
Extract BlockDraggable Component
youknowriad Mar 23, 2018
7f01121
Destructre the uid block prop
youknowriad Mar 26, 2018
9982bcd
Clarify BlockDraggable classnames
youknowriad Mar 26, 2018
5623a71
Clarify Draggable Component Docs
youknowriad Mar 26, 2018
f48d65a
Avoid creating a new block object if the layout didn't change when mo…
youknowriad Mar 26, 2018
d415a1f
Less generic body className
youknowriad Mar 26, 2018
0eca12c
Bind BlockDropZone event handlers to avoid rerenderings
youknowriad Mar 26, 2018
e763e28
Remove Block UI on the cloned block element which fixes drag and scroll
youknowriad Mar 26, 2018
f19dcd0
Decrease the opacity of the cloned draggable
youknowriad Mar 26, 2018
48f8087
Updating ReactTextareraAutosize to fix a bug on initial load (long po…
youknowriad Mar 26, 2018
2759481
Remove iframes from the clone to fix embed's drag and drop
youknowriad Mar 26, 2018
51c1cef
Fix multiple dropzones showing up in Gallery block
youknowriad Mar 27, 2018
794dfd0
Merge remote-tracking branch 'origin/master' into add/drag-n-drop-for…
youknowriad Mar 30, 2018
4a817c5
Changes per review
youknowriad Mar 30, 2018
7dcc860
Fix scrolling when dragging
youknowriad Mar 30, 2018
b20b033
Remove useless styles
youknowriad Mar 30, 2018
c61c770
Merge remote-tracking branch 'origin/master' into add/drag-n-drop-for…
youknowriad Apr 2, 2018
d5daf30
Fix wide blocks scroll
youknowriad Apr 3, 2018
a648f67
Fix typos in documentation
youknowriad Apr 3, 2018
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
11 changes: 10 additions & 1 deletion components/drop-zone/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,12 @@ class DropZoneProvider extends Component {
// This seemingly useless line has been shown to resolve a Safari issue
// where files dragged directly from the dock are not recognized
event.dataTransfer && event.dataTransfer.files.length; // eslint-disable-line no-unused-expressions

const { position, hoveredDropZone } = this.state;
const dropzone = hoveredDropZone !== -1 ? this.dropzones[ hoveredDropZone ] : null;

this.resetDragState();

if ( !! dropzone && !! dropzone.onDrop ) {
dropzone.onDrop( event, position );
}
Expand All @@ -174,7 +177,13 @@ class DropZoneProvider extends Component {
return;
}

if ( event.dataTransfer && !! dropzone && !! dropzone.onFilesDrop ) {
if (
!! dropzone &&
!! dropzone.onFilesDrop &&
event.dataTransfer &&
event.dataTransfer.files &&
event.dataTransfer.files.length
) {
dropzone.onFilesDrop( Array.prototype.slice.call( event.dataTransfer.files ), position );
}

Expand Down
9 changes: 8 additions & 1 deletion components/dropdown/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,17 @@ class Dropdown extends Component {
className,
contentClassName,
expandOnMobile,
...additionalProps,
Copy link
Member

Choose a reason for hiding this comment

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

Why a blanket additionalProps, instead of listing the specific div props here? Also, you might want to update the component's README.md, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

} = this.props;
const args = { isOpen, onToggle: this.toggle, onClose: this.close };
return (
<div className={ className } ref={ this.bindContainer }>
<div
className={ className }
ref={ this.bindContainer }
draggable={ additionalProps.draggable }
onDragStart={ additionalProps.onDragStart }
onDragEnd={ additionalProps.onDragEnd }
>
{ renderToggle( args ) }
<Popover
className={ contentClassName }
Expand Down
1 change: 1 addition & 0 deletions components/placeholder/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
width: 100%;
max-width: 280px;
flex-wrap: wrap;
z-index: z-index( '.components-placeholder__fieldset' );

p {
font-family: $default-font;
Expand Down
8 changes: 8 additions & 0 deletions editor/assets/stylesheets/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ $z-layers: (
'.editor-header': 30,
'.editor-text-editor__formatting': 30,

// Should have lower index than anything else positioned inside the block containers
'.editor-block-list__block-drag-inset': 0,
// Should have higher index than anything else positioned inside the block containers
'.editor-block-list__block-drag-inset.is-visible > .inner': 1000000000,

// Should have higher index than the inset/underlay used for dragging
'.components-placeholder__fieldset': 1,

// Show drop zone above most standard content, but below any overlays
'.components-drop-zone': 100,
'.components-drop-zone__content': 110,
Expand Down
5 changes: 5 additions & 0 deletions editor/assets/stylesheets/main.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
body.dragging .gutenberg > #editor {
cursor: move;/* Fallback for IE/Edge < 14 */
cursor: grabbing !important;
}

body.gutenberg-editor-page {
background: $white;

Expand Down
24 changes: 24 additions & 0 deletions editor/components/block-drop-zone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { compose } from '@wordpress/element';
* Internal dependencies
*/
import { insertBlocks } from '../../store/actions';
import { BLOCK_REORDER } from '../../store/constants';

function BlockDropZone( { index, isLocked, ...props } ) {
if ( isLocked ) {
Expand Down Expand Up @@ -43,9 +44,32 @@ function BlockDropZone( { index, isLocked, ...props } ) {
}
};

const reorderBlock = ( event, position ) => {
if ( index !== undefined && event.dataTransfer ) {
Copy link
Member

Choose a reason for hiding this comment

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

We can avoid unnecessarily tabbing the rest of this function (harms readability) by inversing this condition and turning it into an early return:

if ( index === undefined || ! event.dataTransfer ) {
	return;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely, much cleaner - will refactor this. :-)

try {
const { uid, fromIndex, type } = JSON.parse( event.dataTransfer.getData( 'text' ) );

if ( type !== BLOCK_REORDER ) {
return;
}

if ( position.y === 'top' && index > fromIndex ) {
props.onDrop( uid, index - 1 );
return;
Copy link
Member

Choose a reason for hiding this comment

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

Rather than returning in both of these if conditions, why not move the onDrop into an else if that's effectively what you're trying to express?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Absolutely :-)

} else if ( position.y === 'bottom' && index < fromIndex ) {
props.onDrop( uid, index + 1 );
return;
}

props.onDrop( uid, index );
} catch ( err ) { }
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need the try/catch and does it need to be so broad? I would love some of it to be communicated somehow in the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

JSON.parse can potentially throw an error here. I would keep the error handler and just wrap the respective statement instead to make it clearer.

}
};

return (
<DropZone
onFilesDrop={ dropFiles }
onDrop={ reorderBlock }
/>
);
}
Expand Down
149 changes: 144 additions & 5 deletions editor/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
stopTyping,
updateBlockAttributes,
toggleSelection,
moveBlockToIndex,
} from '../../store/actions';
import {
getBlock,
Expand All @@ -63,6 +64,7 @@ import {
isTyping,
getBlockMode,
} from '../../store/selectors';
import { BLOCK_REORDER } from '../../store/constants';

const { BACKSPACE, ESCAPE, DELETE, ENTER, UP, RIGHT, DOWN, LEFT } = keycodes;

Expand Down Expand Up @@ -101,19 +103,23 @@ export class BlockListBlock extends Component {
this.maybeStartTyping = this.maybeStartTyping.bind( this );
this.stopTypingOnMouseMove = this.stopTypingOnMouseMove.bind( this );
this.mergeBlocks = this.mergeBlocks.bind( this );
this.insertBlocksAfter = this.insertBlocksAfter.bind( this );
this.reorderBlock = this.reorderBlock.bind( this );
this.onFocus = this.onFocus.bind( this );
this.onPointerDown = this.onPointerDown.bind( this );
this.onKeyDown = this.onKeyDown.bind( this );
this.onBlockError = this.onBlockError.bind( this );
this.insertBlocksAfter = this.insertBlocksAfter.bind( this );
this.onTouchStart = this.onTouchStart.bind( this );
this.onClick = this.onClick.bind( this );
this.onDragStart = this.onDragStart.bind( this );
this.onDragEnd = this.onDragEnd.bind( this );

this.previousOffset = null;
this.hadTouchStart = false;

this.state = {
error: null,
dragging: false,
};
}

Expand Down Expand Up @@ -348,6 +354,98 @@ export class BlockListBlock extends Component {
this.setState( { error } );
}

onDragStart( event ) {
const dragInset = document.getElementById( `block-drag-inset-${ this.props.uid }` );
const block = document.getElementById( `block-${ this.props.uid }` );

// Closure to remove the cloned node from the DOM (fired within timeout below)
Copy link
Member

Choose a reason for hiding this comment

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

Why is it fired with a 0 timeout?

Copy link
Contributor Author

@chriskmnds chriskmnds Jan 16, 2018

Choose a reason for hiding this comment

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

This fires after the setDragImage call, which we need to prioritise first. It won't work otherwise - the clone will be removed before the drag image is set.

const removeBlockClone = ( blockList, cloneWrapper ) => {
return () => {
blockList.removeChild( cloneWrapper );
};
};

// Closure to hide the visible block and show inset in its place (fired within timeout below)
const showInset = () => {
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this to be a function that returns a function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope - at least not in its current state. It was accepting parameters initially, but not since I switched to updating state here. I will refactor this eventually, just not sure right now if a state variable is the way to go here - I do notice some inconsistencies in IE, which I need to investigate.

Copy link
Member

Choose a reason for hiding this comment

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

👍

return () => {
this.setState( { dragging: true } );
document.body.classList.add( 'dragging' );
Copy link
Member

Choose a reason for hiding this comment

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

Does this piece of information need to be kept at body level?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure and I still need to work this out - it currently doesn't work as I like. Whatever I seem to try, the handler just changes randomly to "copy" in the middle of dragging sometimes.

Copy link
Member

Choose a reason for hiding this comment

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

Whatever I seem to try, the handler just changes randomly to "copy" in the middle of dragging sometimes.

Why does this happen? Also, what have you tried?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey, I will focus on this as my next in line task. It's been sitting in my backlog for some time to be specific right now, but I will share details (or solution!) soon 😃

Copy link
Contributor Author

@chriskmnds chriskmnds Jan 21, 2018

Choose a reason for hiding this comment

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

Ok, should be fixed now - had to add a fake drag image to avoid browser defaults with dragging (Chrome changes the cursor apparently). Similar issue to what these folks seem to be having: SortableJS/Sortable#246

I've added .dragging to the body, cause we want the cursor change to be document wide.

Any other concerns/suggestions on this?

};
};

event.dataTransfer.setData(
'text',
Copy link
Member

Choose a reason for hiding this comment

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

If I drag an image block to my desktop I get a JSON file with this internal representation. Low-priority, but I am curious whether we can somehow affect that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch! I will investigate.

JSON.stringify( {
uid: this.props.uid,
fromIndex: this.props.order,
type: BLOCK_REORDER,
} )
);

if ( 'function' === typeof event.dataTransfer.setDragImage ) {
Copy link
Member

Choose a reason for hiding this comment

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

In what cases this won't be true?

Copy link
Member

Choose a reason for hiding this comment

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

This block makes onDragStart quite long and hard to follow for me – its purpose is not obvious until I have reached the middle of it (moving the clone around).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In what cases this won't be true?

Oh man, don't ask. IE11!

This block makes onDragStart quite long and hard to follow for me – its purpose is not obvious until I have reached the middle of it (moving the clone around).

Totally agree this will be refactored in the end.

const blockList = block.parentNode;
const cloneWrapper = document.createElement( 'div' );
const clone = block.cloneNode( true );
const blockRect = block.getBoundingClientRect();
const blockTopOffset = parseInt( blockRect.top, 10 );
Copy link
Member

Choose a reason for hiding this comment

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

Indentation here seems a bit off.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks - spotted a few other linting errors too, but should be cleared in latest commit. Just run the linter and all green. 😃

Copy link
Member

Choose a reason for hiding this comment

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

👍

const blockLeftOffset = parseInt( blockRect.left, 10 );

// 1. Clone the current block, style it, and spawn over original block.

clone.id = `clone-${ block.id }`;

cloneWrapper.classList.add( 'editor-block-list__block-clone' );
// 40px padding for the shadow.
cloneWrapper.style.width = `${ parseInt( blockRect.width, 10 ) + 40 }px`;
// Position clone right over the original block.
// 20px padding for the shadow.
cloneWrapper.style.top = `${ blockTopOffset - 20 }px`;
cloneWrapper.style.left = `${ blockLeftOffset - 20 }px`;

cloneWrapper.appendChild( clone );
blockList.appendChild( cloneWrapper );

// 2. Set clone as drag image and remove from DOM:

// Display the drag image right over the block:
// - Coordinates relative to the block and the cursor/
// - Optimisation: if top ends of the block are outside the viewport,
// then revert to top 0 (relative to cursor).
const top = blockTopOffset > 0 ?
parseInt( event.clientY, 10 ) - blockTopOffset + 20 :
parseInt( event.clientY, 10 ) + 20;

event.dataTransfer.setDragImage(
cloneWrapper,
parseInt( event.clientX, 10 ) - blockLeftOffset + 20,
top
);

setTimeout( removeBlockClone( blockList, cloneWrapper ), 0 );
}

// Hide the visible block and show inset in its place.
setTimeout( showInset(), 0 );
}

onDragEnd() {
const block = document.getElementById( `block-${ this.props.uid }` );
const dragInset = document.getElementById( `block-drag-inset-${ this.props.uid }` );

const resetBlockDisplay = () => {
return () => {
this.setState( { dragging: false } );
document.body.classList.remove( 'dragging' );
};
}

setTimeout( resetBlockDisplay(), 0 );
}

reorderBlock( uid, toIndex ) {
this.props.moveBlockToIndex( uid, toIndex );
}

render() {
const { block, order, mode, showContextualToolbar, isLocked } = this.props;
const { name: blockName, isValid } = block;
Expand All @@ -360,13 +458,17 @@ export class BlockListBlock extends Component {
// Generate the wrapper class names handling the different states of the block.
const { isHovered, isSelected, isMultiSelected, isFirstMultiSelected, focus } = this.props;
const showUI = isSelected && ( ! this.props.isTyping || ( focus && focus.collapsed === false ) );
const { error } = this.state;
const { error, dragging } = this.state;
const wrapperClassName = classnames( 'editor-block-list__block', {
'has-warning': ! isValid || !! error,
'is-selected': showUI,
'is-multi-selected': isMultiSelected,
'is-hovered': isHovered,
'is-reusable': isReusableBlock( blockType ),
'is-hidden': dragging,
Copy link
Member

Choose a reason for hiding this comment

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

Is this a concern of the consumer of Draggable ? Seems to require a lot of work to use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is correct - showing an overlay in place of the original content or some other style deviation is the concern of the consumer.

} );
const blockDragInsetClassName = classnames( 'editor-block-list__block-drag-inset', {
'is-visible': dragging
} );

const { onMouseLeave, onFocus, onReplace } = this.props;
Expand All @@ -381,6 +483,7 @@ export class BlockListBlock extends Component {
/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
return (
<div
id={ `block-${ this.props.uid }` }
ref={ this.setBlockListRef }
onMouseMove={ this.maybeHover }
onMouseEnter={ this.maybeHover }
Expand All @@ -391,9 +494,37 @@ export class BlockListBlock extends Component {
onClick={ this.onClick }
{ ...wrapperProps }
>
<BlockDropZone index={ order } />
{ ( showUI || isHovered ) && <BlockMover uids={ [ block.uid ] } /> }
{ ( showUI || isHovered ) && <BlockSettingsMenu uids={ [ block.uid ] } /> }
<div
id={ `block-drag-inset-${ this.props.uid }` }
draggable={ true }
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: we can drop the ={ true}

onDragStart={ this.onDragStart }
onDragEnd={ this.onDragEnd }
className={ blockDragInsetClassName }
>
<div className="inner" ></div>
</div>

<BlockDropZone
index={ order }
onDrop={ this.reorderBlock }
/>

{ ( showUI || isHovered ) &&
<BlockMover
draggable={ true }
onDragStart={ this.onDragStart }
Copy link
Member

Choose a reason for hiding this comment

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

Do these events need to be applied to every one of the child components? Can't we add a single drag event to the root wrapper component? If it's an issue of competing with onMouseDown... well, I don't like that we use onMouseDown, so maybe there's a refactoring opportunity there.

Copy link
Contributor Author

@chriskmnds chriskmnds Feb 21, 2018

Choose a reason for hiding this comment

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

Arghh, I wish there was an easier way to accomplish this whole effect. The way I have designed the logic to handle dragging is we need to apply these methods to everything that we want to use as a drag handle (a point that the user can grab something from). They will behave the same way regardless of where they are applied. This way we don't have to experiment with an overlay that will interfere with other drag logic (i.e. resizing an image, selecting text, or anything else).

So to answer your question: these events (returned from the closures in withDragging) need to be applied to every area of the UI that we want to use as a drag handle. Irrespective of where it is.

onDragEnd={ this.onDragEnd }
uids={ [ block.uid ] }
/>
}
{ ( showUI || isHovered ) &&
<BlockSettingsMenu
draggable={ true }
onDragStart={ this.onDragStart }
onDragEnd={ this.onDragEnd }
uids={ [ block.uid ] }
/>
}
{ showUI && isValid && showContextualToolbar && <BlockContextualToolbar /> }
{ isFirstMultiSelected && <BlockMultiControls /> }
<div
Expand All @@ -407,6 +538,7 @@ export class BlockListBlock extends Component {
tabIndex="0"
aria-label={ blockLabel }
>

<BlockCrashBoundary onError={ this.onBlockError }>
{ isValid && mode === 'visual' && (
<BlockEdit
Expand Down Expand Up @@ -469,6 +601,7 @@ const mapDispatchToProps = ( dispatch, ownProps ) => ( {
onSelect() {
dispatch( selectBlock( ownProps.uid ) );
},

onDeselect() {
dispatch( clearSelectedBlock() );
},
Expand All @@ -488,6 +621,7 @@ const mapDispatchToProps = ( dispatch, ownProps ) => ( {
uid: ownProps.uid,
} );
},

onMouseLeave() {
dispatch( {
type: 'TOGGLE_BLOCK_HOVERED',
Expand Down Expand Up @@ -519,9 +653,14 @@ const mapDispatchToProps = ( dispatch, ownProps ) => ( {
onMetaChange( meta ) {
dispatch( editPost( { meta } ) );
},

toggleSelection( selectionEnabled ) {
dispatch( toggleSelection( selectionEnabled ) );
},

moveBlockToIndex( uid, index ) {
dispatch( moveBlockToIndex( uid, index ) );
},
} );

BlockListBlock.className = 'editor-block-list__block-edit';
Expand Down
Loading