-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Use the breadcrumb as the draggable handle #8764
Closed
Closed
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
add68b9
Debug: colors are fun!
oandregal d4f6fa7
Debug: make the draggable area visible
oandregal d06670f
We're going to get rid of side grabs
oandregal c119acb
Match breadcrumb positioning
oandregal 32a465f
Position the handle in the top right corner, alongside the label
oandregal 97f17ea
Expose the handle, hide the contents
oandregal 9c694b6
Dont make the handle ever visible.
oandregal 123d37b
Add grab affordance
oandregal a37bcde
HACK: PoC to improve the draggable indicator
oandregal fcd43b7
Use opacity to communicate what block is being dragged.
oandregal ebfe05a
Create withDraggable HOC
oandregal ca3dbf6
Refactor breadcrumb to use withDraggable HOC
oandregal 4cea178
Deprecate wp.components.Draggable in favor of wp.components.withDragg…
oandregal 47f02fe
fixup docs
oandregal 74d1b9c
Remove block-draggable
oandregal 77f7ef1
Add comment to z-index value
oandregal 26ca9bf
Make cursor affordance visible
oandregal ac43bb3
withDraggable injects initDragging instead of onDragStart
oandregal 7c37b06
Breadcrumb notifies parent on drag start and end
oandregal 9d4793a
Move deprecations to 3.7 version
oandregal 9999a4f
The breadcrumb should not be shown above overlays
oandregal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
packages/components/src/higher-order/with-draggable/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# withDraggable | ||
|
||
`withDraggable` is a Higher-Order Component that provides a way to set up a cross-browser (including IE) customisable drag image, and the transfer data for the drag event. It decouples the drag handle and the element to drag: it wraps the component that will be the drag handle, and it should be provided the DOM ID of the element to drag. | ||
|
||
Note that the drag handle needs to declare the `draggable="true"` property. The `withDraggable` component only takes care of the logic to setup the drag image and the transfer data, but is not concerned with creating an actual DOM element that is draggable. | ||
|
||
## Props | ||
|
||
The component injects the following props into the wrapped component: | ||
|
||
### initDragging | ||
|
||
A function that initializes the drag event, setting up the transfer data and creating the drag image. It returns a function to be called on the `dragstart` DOM event. | ||
|
||
- Type: `Function` | ||
- Required: Yes | ||
- Arguments: | ||
- `elementId`: DOM id of the element to be dragged | ||
- `data`: the [data](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/dataTransfer) to be transfered by the event | ||
|
||
## Usage | ||
|
||
```jsx | ||
import { Dashicon, Panel, PanelBody, withDraggable } from '@wordpress/components'; | ||
|
||
const MyDraggable = ( { initDragging } ) => ( | ||
<div id="draggable-panel"> | ||
<Panel header="Draggable panel" > | ||
<PanelBody> | ||
<Dashicon | ||
icon="move" | ||
draggable="true" | ||
onDragStart={ initDragging( "draggable-panel", {} ) } /> | ||
</PanelBody> | ||
</Panel> | ||
</div> | ||
); | ||
|
||
export default withDraggable( MyDraggable ); | ||
``` | ||
|
||
If the wrapped element would want to inject their own logic into the `dragstart` event, it should initialize the `initDragging` prop provided by `withDraggable` in that event handler. For example: | ||
|
||
```jsx | ||
|
||
import { Dashicon, Panel, PanelBody, withDraggable } from '@wordpress/components'; | ||
|
||
const class MyDraggable extends Component { | ||
|
||
constructor() { | ||
super( ...arguments ); | ||
this.myDragStart = this.myDragStart.bind( this ); | ||
} | ||
|
||
myDragStart( event ){ | ||
this.props.initDragging( 'draggable-panel', {} )( event ); | ||
|
||
// can do whatever is necessary after the event has been set up | ||
} | ||
|
||
render( ) { | ||
return ( | ||
<div id="draggable-panel"> | ||
<Panel header="Draggable panel" > | ||
<PanelBody> | ||
<Dashicon | ||
icon="move" | ||
draggable="true" | ||
onDragStart={ myDragStart } | ||
/> | ||
</PanelBody> | ||
</Panel> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default withDraggable( MyDraggable ); | ||
``` |
154 changes: 154 additions & 0 deletions
154
packages/components/src/higher-order/with-draggable/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/** | ||
* WordPress Dependencies | ||
*/ | ||
import { Component } from '@wordpress/element'; | ||
import { createHigherOrderComponent } from '@wordpress/compose'; | ||
|
||
const dragImageClass = 'components-draggable__invisible-drag-image'; | ||
const cloneWrapperClass = 'components-draggable__clone'; | ||
const cloneHeightTransformationBreakpoint = 700; | ||
const clonePadding = 20; | ||
|
||
const withDraggable = createHigherOrderComponent( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, this component ports the existing |
||
( OriginalComponent ) => { | ||
return class extends Component { | ||
constructor() { | ||
super( ...arguments ); | ||
this.onDragStart = this.onDragStart.bind( this ); | ||
this.onDragOver = this.onDragOver.bind( this ); | ||
this.onDragEnd = this.onDragEnd.bind( this ); | ||
this.resetDragState = this.resetDragState.bind( this ); | ||
} | ||
|
||
componentWillUnmount() { | ||
this.resetDragState(); | ||
} | ||
|
||
/** | ||
* Function that creates the dragstart event handler. | ||
* | ||
* @param {string} elementId The HTML id of the element to be dragged. | ||
* @param {Object} data The data to be set to the event's dataTransfer - to be accessible in any later drop logic. | ||
* @return {function} A function for wrapped components to use as their onDragStart handler. | ||
*/ | ||
onDragStart( elementId, data ) { | ||
return ( event ) => { | ||
const element = document.getElementById( elementId ); | ||
if ( ! element || ! data ) { | ||
event.preventDefault(); | ||
return; | ||
} | ||
|
||
event.dataTransfer.setData( 'text', JSON.stringify( data ) ); | ||
|
||
// Set a fake drag image to avoid browser defaults. Remove from DOM | ||
// right after. event.dataTransfer.setDragImage is not supported yet in | ||
// IE, we need to check for its existence first. | ||
if ( 'function' === typeof event.dataTransfer.setDragImage ) { | ||
const dragImage = document.createElement( 'div' ); | ||
dragImage.id = `drag-image-${ elementId }`; | ||
dragImage.classList.add( dragImageClass ); | ||
document.body.appendChild( dragImage ); | ||
event.dataTransfer.setDragImage( dragImage, 0, 0 ); | ||
setTimeout( () => { | ||
document.body.removeChild( dragImage ); | ||
} ); | ||
} | ||
|
||
// Prepare element clone and append to element wrapper. | ||
const elementRect = element.getBoundingClientRect(); | ||
const elementWrapper = element.parentNode; | ||
const elementTopOffset = parseInt( elementRect.top, 10 ); | ||
const elementLeftOffset = parseInt( elementRect.left, 10 ); | ||
const clone = element.cloneNode( true ); | ||
clone.id = `clone-${ elementId }`; | ||
this.cloneWrapper = document.createElement( 'div' ); | ||
this.cloneWrapper.classList.add( cloneWrapperClass ); | ||
this.cloneWrapper.style.width = `${ elementRect.width + ( clonePadding * 2 ) }px`; | ||
|
||
if ( elementRect.height > cloneHeightTransformationBreakpoint ) { | ||
// Scale down clone if original element is larger than 700px. | ||
this.cloneWrapper.style.transform = 'scale(0.5)'; | ||
this.cloneWrapper.style.transformOrigin = 'top left'; | ||
// Position clone near the cursor. | ||
this.cloneWrapper.style.top = `${ event.clientY - 100 }px`; | ||
this.cloneWrapper.style.left = `${ event.clientX }px`; | ||
} else { | ||
// Position clone right over the original element (20px padding). | ||
this.cloneWrapper.style.top = `${ elementTopOffset - clonePadding }px`; | ||
this.cloneWrapper.style.left = `${ elementLeftOffset - clonePadding }px`; | ||
} | ||
|
||
// Hack: Remove iFrames as it's causing the embeds drag clone to freeze | ||
[ ...clone.querySelectorAll( 'iframe' ) ].forEach( ( child ) => child.parentNode.removeChild( child ) ); | ||
|
||
this.cloneWrapper.appendChild( clone ); | ||
elementWrapper.appendChild( this.cloneWrapper ); | ||
|
||
// Mark the current cursor coordinates. | ||
this.cursorLeft = event.clientX; | ||
this.cursorTop = event.clientY; | ||
// Update cursor to 'grabbing', document wide. | ||
document.body.classList.add( 'is-dragging-components-draggable' ); | ||
|
||
// connect listeners | ||
document.addEventListener( 'dragover', this.onDragOver ); | ||
document.addEventListener( 'dragend', this.onDragEnd ); | ||
}; | ||
} | ||
|
||
/** | ||
* Updates positioning of element clone based on mouse movement during dragging. | ||
* @param {Object} event The non-custom DragEvent. | ||
*/ | ||
onDragOver( event ) { | ||
this.cloneWrapper.style.top = | ||
`${ parseInt( this.cloneWrapper.style.top, 10 ) + event.clientY - this.cursorTop }px`; | ||
this.cloneWrapper.style.left = | ||
`${ parseInt( this.cloneWrapper.style.left, 10 ) + event.clientX - this.cursorLeft }px`; | ||
|
||
// Update cursor coordinates. | ||
this.cursorLeft = event.clientX; | ||
this.cursorTop = event.clientY; | ||
} | ||
|
||
/** | ||
* Removes the element clone, resets cursor, and removes drag listener. | ||
*/ | ||
onDragEnd( ) { | ||
this.resetDragState(); | ||
} | ||
|
||
/** | ||
* Cleans up drag state when drag has completed, or component unmounts | ||
* while dragging. | ||
*/ | ||
resetDragState() { | ||
// Remove listeners | ||
document.removeEventListener( 'dragover', this.onDragOver ); | ||
document.removeEventListener( 'dragend', this.onDragEnd ); | ||
|
||
// Remove drag clone | ||
if ( this.cloneWrapper && this.cloneWrapper.parentNode ) { | ||
this.cloneWrapper.parentNode.removeChild( this.cloneWrapper ); | ||
this.cloneWrapper = null; | ||
} | ||
|
||
// Reset cursor. | ||
document.body.classList.remove( 'is-dragging-components-draggable' ); | ||
} | ||
|
||
render() { | ||
return ( | ||
<OriginalComponent | ||
initDragging={ this.onDragStart } | ||
{ ...this.props } | ||
/> | ||
); | ||
} | ||
}; | ||
}, | ||
'withDraggable' | ||
); | ||
|
||
export default withDraggable; |
20 changes: 20 additions & 0 deletions
20
packages/components/src/higher-order/with-draggable/style.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
body.is-dragging-components-draggable { | ||
cursor: move;/* Fallback for IE/Edge < 14 */ | ||
cursor: grabbing !important; | ||
} | ||
|
||
.components-draggable__invisible-drag-image { | ||
position: fixed; | ||
left: -1000px; | ||
height: 50px; | ||
width: 50px; | ||
} | ||
|
||
.components-draggable__clone { | ||
position: fixed; | ||
padding: 20px; | ||
background: transparent; | ||
pointer-events: none; | ||
z-index: z-index(".components-draggable__clone"); | ||
opacity: 0.8; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 0 additions & 31 deletions
31
packages/editor/src/components/block-list/block-draggable.js
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes to the dashicon component is an interim hack. We need to either port them to upstream dashicon repo if they make sense or figure out a different approach to show the drag affordance to the user.