-
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
Drop zone: rewrite with hooks (useDropZone) #19514
Conversation
69f98a0
to
e8bf37d
Compare
Performance test:
|
e8bf37d
to
9d3667b
Compare
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.
- When you start a new post, dropping a file on the post placeholder doesn't work.
getClientIdsOfDescendants, | ||
hasUploadPermissions, | ||
isLockedAll, | ||
} = useSelect( selector, [ rootClientId ] ); |
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.
What pattern did we settle on? I thought we decided to use inline functions?
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.
I thought we settled on allowing it. It's especially handy if variable names are clashing. It's also an optimisation if useSelect
has no dependencies, you can move it out of the component. Here we depend on rootClientId
though.
packages/block-editor/src/components/block-list-appender/index.js
Outdated
Show resolved
Hide resolved
import { DropZoneConsumer } from './provider'; | ||
import { DropZoneConsumer, Context } from './provider'; | ||
|
||
export function useDropZone( { element, onFilesDrop, onHTMLDrop, onDrop, isDisabled } ) { |
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.
Should we add some JSDocs and types :p cc @aduth
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.
Should we move this to @wordpress/compose
like all other hooks? I guess the problem is that it's dependent on the DropzoneProvider so I'm on the fence. I know the provider is necessary for performance reasons, other than that it would be good to kill.
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.
I haven't looked into the DropzoneProvider
, but as long as we need that, this seems like the right place. We can prefix it with __unstable
if we want to keep our options open.
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.
I prefixed it. I will have another look separately to move it and look into the provider.
{ shouldRenderDropzone && <BlockDropZone | ||
clientId={ clientId } | ||
rootClientId={ rootClientId } | ||
/> } |
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 main problem with this PR I think is that the dropzone is smaller than before. In master for paragraphs, its size is "37px" with this pr it is "30px". They both suffer from the same issue, there's a space (margin) between blocks where you can't drop blocks. It's is more visible in this PR because of the smaller size though.
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.
@youknowriad I fixed the issue entirely by rewriting useBlockDropZone
to work on BlockList
instead of BlockListBlock
. Now there are no longer "dead zones" between the blocks where you can't drop.
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 is cool, I'll test a bit 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.
Thanks! I think the error might be gone too, but let me know if it isn't. :)
@youknowriad Thanks, I'll fix the issues! |
const offset = y - rect.top; | ||
const target = Array.from( element.current.children ).find( ( blockEl ) => { | ||
return blockEl.offsetTop + ( blockEl.offsetHeight / 2 ) > offset; | ||
} ); |
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 it possible to take into consideration the blocks that are laid out horizontally (I'm not sure it was the case before)
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.
It's not possible in master
right now, so I'd prefer to tackle this in a separate PR to keep this small and contained.
let x = detail.clientX; | ||
let y = detail.clientY; | ||
|
||
if ( ! hoveredDropZone.withExactPosition ) { |
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.
should we just use another property like "coordinates" instead of hacking two different types in the same property?
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.
I personally wouldn't, because it will make the component rerender much more, but I'm fine with it either way. We could remove the top/bottom calculation since they were specific for blocks.
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.
Although is-close-to-top
is part of DropZone
's API. Wouldn't mind removing it if it's ok, nothing in Gutenberg is now using these classes.
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.
I don't mind dropping it too if not used. (dev note)
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.
Ok, I removed them. :)
Usability wise, this feels way better than master. I'd appreciate some testing from @jasmussen |
4c4b22a
to
257a093
Compare
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.
Thanks a lot for the reviews! Let's merge and iterate. I think this improves things in many ways. |
I am so excited for this PR — the old drag-and-drop behavior was quite finicky but this looks like a tremendous improvement! |
I don't know if this was addressed, but I'm pretty sure it behaved this way (i.e. didn't allow drop on a new post) prior to the changes here. |
I double checked that you can drop on the appender, but right, you can't drop on the rest of the document. |
The dropzone behavior is so much more stable with this change, but I have to ask, is the |
I think we need to see how media upload can best be filtered. I think the drop zone is the wrong place for a hook because (1) there’s more places where media can be uploaded/inserted (paste, placeholder…) and (2) the drop zone handles more than just media, it also handles internal block dropping. @plaidpowered What were you using the hook for? |
Thanks for getting back to me! Thankfully no production code has been broken, but as the project was in progress, I wanted to ensure my work is future-proof. I may have to go at it from a different direction. |
@plaidpowered Would be good to see it in action with a GIF for example, to better understand what you mean. Let me know if there's anything in particular you'd like to filter after this change. Any props in |
Actually, perhaps even better than a filter would be to add an action at the end of
For my specific use case, that's literally all I'd need. Of course, this wouldn't filter onDrop behavior, simply allow actions to be performed after blocks are rearranged, and I imagine there would be multiple use cases beyond my own. For instance, if a developer wants to validate whether a block is allowed to be placed in a certain location. I really appreciate you discussing this with me, thanks! |
What are you doing after the blocks have rearranged? I don't get what this "option to swap two blocks" is. :) |
It depends on a number of factors, some of which I haven't figured out yet - the idea is that if you have block a and block b, and drag block a onto block b, block a will take the location of block b and block b will be moved to the former location of block a. I'm still working out the UI for this. Ideally I want a dedicated area in block b's dropzone, and if the block a is dropped there, the replacement/swap occurs instead of the standard insert behavior. But if I can't make that work with the present codebase, I'll present some kind of dialog to the user asking if they want to Insert (before the dst block), Swap (dst with src), or Replace (dst is deleted, src takes its place). Admittedly, the old |
The dragging and dropping of blocks already swaps the blocks, no? |
I suppose it does if you are dragging and dropping blocks within the same parent container, and the two blocks are adjacent to each other, but if you are moving blocks between two different containers, the present behavior simply inserts the dragged block to before the destination dropzone. My intention is to have the dragged block and destination block trade places, even if they are in different containers. |
Hi again @ellatrix Anyway, thanks for your help! It may still be valuable to include some filters in this functionality. Filters are the best. :D |
Description
This PR removes the last remaining control element from the block DOM. It looks like it offers a further 10% performance improvement on typing.
I created a
useDropZone
hook alternative that allows you to create a drop zone without needing to create an additional element. It works by passing the reference to the target element to the hook.How has this been tested?
Screenshots
Types of changes
Checklist: