-
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
Refactor Draggable to decouple drag handle from the DOM node being dragged #9311
Conversation
const cloneHeightTransformationBreakpoint = 700; | ||
const clonePadding = 20; | ||
|
||
const withDraggable = createHigherOrderComponent( |
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.
Note that this code has been ported with no substantial modifications from the Draggable
component.
class BlockDraggable extends Component { | ||
render() { | ||
const { clientId, elementId, index, initDragging, isDragging, layout, rootClientId } = this.props; | ||
const className = classnames( 'components-draggable', 'editor-block-list__block-draggable', { |
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.
Do we need the components-draggable
class?
0e10f1e
to
eb5f57c
Compare
layout, | ||
}; | ||
initDragging( elementId, transferData )( event ); | ||
setTimeout( () => onDragStart( event ) ); |
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.
Note that we need to make sure onDragStart
is executed after initDragging
.
This is due to how Draggable
and Drag and Drop behavior was originally set up. The isDragging
prop takes its value from state.dragging
which is changed in the onDragStart
and onDragEnd
methods of the block. Upon isDragging
being true, the visibility of any block's children is changed to hidden
, making them un-reactive to events (so the initDragging
handler ends up not being set up).
In the next iterations, when the drag handle doesn't have a dedicated DOM node but uses an existing component, this hack should be gone.
Technically this PR isn't a breaking change, but it is going to be once the deprecation is removed. |
Great work and I can see how decoupling the rendering of the DragHandle is a good idea. Though, there's something bothering me about the What I propose is to have this: const DragHandle = ( { onDragStart, onDragEnd } ) => {
return (
<button { ...{ onDragStart, onDragEnd } }>Drag Handle</button>
);
}
export withDraggable(( props ) => {
transfertData: props.data,
clonedElement: props.clonedElement
} ) But if you take a look at this (above), it's the exact API as the <Draggable transfertData={ data } clonedElement={ element }>
{ ( { onDragStart, onDragEnd } ) => (
<button { ...{ onDragStart, onDragEnd } }>Drag Handle</button>
) }
</Draggable> And this "backwards compatible" because we can check that if Thoughts? |
eb5f57c
to
1185662
Compare
@youknowriad I like that! It also solves some concerns I had regarding the API leaking some important details. See 6e4fd6f and 1185662 messages. Implemented and pushed that API change. I can modify docs later, once we agreed to the API. |
I think we can reduce the changes required to implement that if we just add the "children as function" support to the current |
I'm open to either use the
|
@youknowriad We commented at the same time :) What do you think of #9311 (comment) ? |
I totally understand this. However, I'd rather reframe the debate to optimize what's best from an user perspective (coherence with the rest of the codebase, easiness of use, etc). I'm undecided, and would love to hear your thoughts about why a render prop in |
I'd argue that the HoC is always more complex to understand than a simple component. Granted that children as function is not "simple" but I personally think it's easier to understand than a function
We already use Render Props in
You can wrap the whole component in a HoC and only pass the injected props to a single element inside this component. I'd say that the disconnect between the place you apply the component and the element where the props are applied is more "obvious" when using I'm not saying the render prop is always superior to an HoC and I guess it can mostly be considered a personal preference, my personal preference in this particular use case though is towards a render prop. Because: 1- We already have a component 2- We can have a default render function which means if we don't want to control the rendering of the draggable handle we can just drop the |
1185662
to
e804f80
Compare
@youknowriad Pushed the necessary changes to use I took a step back and considered the API with fresh eyes. I think now the data flow is clearer: |
docs/reference/deprecated.md
Outdated
@@ -14,6 +14,7 @@ Gutenberg's deprecation policy is intended to support backwards-compatibility fo | |||
|
|||
- `wp.components.withContext` has been removed. Please use `wp.element.createContext` instead. See: https://reactjs.org/docs/context.html. | |||
- `wp.coreBlocks.registerCoreBlocks` has been removed. Please use `wp.blockLibrary.registerCoreBlocks` instead. | |||
- `wp.components.Draggable` as a DOM node drag handler has been deprecated. Please, use `wp.components.Draggable` as a wrap component for your DOM node drag handler. |
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.
Is there a better way to explain this change?
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.
This deprecation will be removed in 4.0 release if it is planned for 3.8 release.
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.
Do we need to include the version that will remove the behavior here? I haven't seen it anywhere else.
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.
Just to make sure: the deprecation is added to the version it was deprecated, right? Or should we move this to the 4.0
section?
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.
The current deprecations are listed below and are grouped by the version at which they will be removed completely.
It should be under 4.0 😄
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.
✍️ 32006d3
and let React bind the class methods as well.
To avoid withDraggable's event handlers to be shadowed by the children props we need to make sure they are passed at the end.
It seems to be more performant. API-wise it makes sense that withDraggable takes care of calling children's dragstart/dragend event handlers as it needs them to be executed after its own. The reason for this is that the children's event handlers could modify the DOM element withDraggable clones, messing up with its behavior. For example, if the children's event handler change the visibility of the element that takes the event, in turn the element won't be able to process any further events. We don't have the same restrictions with the dragover event. At that point, it can work independently from the children's.
It is the component that build the necessary transfer data to drag a block around.
53b10d3
to
755e0aa
Compare
icon="move" | ||
onDragStart={ onDraggableStart } | ||
onDragEnd={ onDraggableEnd } | ||
draggable |
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.
FYI @nosolosw: the draggable
attribute is enumerated, so shorthanding it like you have here is actually not valid.
See here: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable
cc @youknowriad
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.
Isn't this something already taken care of by React? we should fix if not :)
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.
@dsifford you're right in that draggable
is enumerated. We could probably the example (and code) to remain as true to the HTML standard as possible. However, note that React does the conversion for us.
Related #7114
Makes #8764 obsolete
This PR refactors the internals of drag-and-drop, to make easier changing the drag handle in next iterations. Note that the external behavior of the drag-and-drop event is not changed in this PR.
The
Draggable
componentIt refactors the
Draggable
component to decouple the drag handle (the wrapped component) from the element being dragged. After this change,Draggable
is no longer be concerned with creating an actual DOM node that serves as drag handle, but the drag handle is the wrapped component itself. This makesDraggable
reusable in use cases other than making a block draggable.The existing behavior is deprecated and planned to be removed on the next version.
How to use it:
Test
Drag and drop a block and test that it works as used to be (the block being dragged is shown as a grey box, the dragging event shows an image that is a clone of the block being dragged).