-
-
Notifications
You must be signed in to change notification settings - Fork 249
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
Feature Proposal: Add global handlers for start, finish and cancel drag #186
Comments
OK, further to writing this yesterday (and attempting to refactor my helper function to do something like my suggestion in #185 and use an extended version of OutlineIt's to do with APIs that require a single "move" call, vs a "remove" and then an "add" (or "add" then "remove"). The browser Extensions API is one; it requires that Tabs and Bookmarks be moved using a specific To start with, here is a basic algorithm to detect the type of action: const action: DragAction | null = removedIndex !== null && addedIndex !== null
? removedIndex !== addedIndex
? 'order'
: null
: addedIndex !== null
? 'add'
: removedIndex !== null
? 'remove'
: null Because of the way Smooth DnD works by calling each container's drop handler in turn, there is no way to know if a "move" has taken place. Items in the same container (works)Take a single container with two items, and we want to drag
Because there is only one container, the Perfect! We call move: if (action === 'order') {
chrome.tabs.move(...)
} Items in different containers (can't work)Now let's look at two containers where we want to move one item to the other container:
In this case, two // container 1
if (action === 'remove') {
chrome.tabs.remove(...)
} // container 2
if (action === 'add') {
chrome.tabs.create(...)
} But, EEEK! – by running separate Because there was no way for the remove event to know that there was an add event coming (or to know that this was a "move" and not a "remove" and then an "add") we are unable to call the right API, which in Chrome's case is chrome.tabs.move(tab, index, windowId) The multiple components problemThe reason this has been working (for me) up to now, is because of my previous helper function which provides additional context (of the removes and adds) at the end of all calls. This works fine if you just have a simple Kanban board, with all operations in one component, because if you have 3 containers, you only take action on the 3rd call (you don't do anything in the first 2 calls):
Only on call 3 when you know also what happened with 1 and 2, do you decide what to do. However, as soon as you have multiple, separate components (which I presume SmoothDnD was specifically designed to work with because of the "calling each container" approach) and you try to use my previous technique, only the last container of all containers gets all the information:
Drop events will fire (I think) in order for If you had moved an item from Additionally, Component 3 may have logic for items only of type In fact this last point helps to make the case for a 100% global handler, which could marshall all containers (or perhaps a global store) and decide what should happen. In the case of something like Vuex or Redux, this would be really useful. ConclusionWhat would allow all containers to decide what they are doing would be a final call to each set, with the complete picture of the adds and removes, so they can decide to do a "move" rather than separate "add" and "remove" calls, so:
WorkaroundsI have a couple of ideas how to solve this right now: Debounce drop eventsFor each component, in the drop handler, collect the results of any drag, then debounce the actual update: onDrop (event) {
this.events.push(event)
this.onDropComplete()
}
onDropComplete: debounce (function() {
// get events
// decide what to do
}, 0) The complete handler would fire once per group, so:
Give the drop handler factory more informationIn the global drop handler factory, pass additional information about the container, then use this to return to each component when all results are in for its group. In the case above, drop handlers for onDrop: makeDropHandler('windows', function (event, ...) {
if (event.isLastInGroup) {
if (event.action === 'move') {
// move
}
}) Does all this make sense? |
OK! So I've implemented all this over the top of Vue Smooth DnD. It actually wasn't too bad! I have all the support I require, so: Global handlers:
Component-level handlers:
Types:
Global state (provided as a plugin, watchable from anywhere):
Rather pleased with this, and it's cleaned up my code no-end! |
Hey @kutlugsahin,
I'm busy on a new slew of features, and have added additional container / container types to my app which has highlighted the lack of global event handlers.
Setup
Lets's say I have 3 sections in my app:
A
,B
,C
Outside of the containers, there might be other parts of the app which might want to know if there is drag and drop:
Problem
Right now:
get-child-payload
orshould-accept-drop
should-accept-drop
then count the number of containers out using@drop
Additionally, there are issues using local code in multiple containers:
shouldAcceptDrop()
is called multiple times (sayA
x 1,B
x 5,C
x 10) any "start" code is fired 16 timesApproach
The easiest way I have found to do this is to provide global handler decorators:
In the component:
This works, but it is a lot of extra code, that feels like it could / should be in the library.
Proposal
I suggest two API changes, to manage local and global events:
Global events
Something like:
The dispatched events could contain some basic information about the drag.
This would allow any components outside of the components which contain the containers, to monitor drags.
In Vue, you could also provide a
DragObserver
component like so:Vee Validate does this to wrap forms for validation, and it works great.
I'm not sure what the equivalent would be in React, but I'm sure there is one.
Another Vue option would be to export a
Vue.observable
and / or provide plugin code from the core:This then be used to get access to the state, add classes on any other containers, etc, etc:
Local handlers
In the same component, a developer could use the Observer component as above, or they could just use handlers:
I'm not sure about the names; I can also think of variations such as
before
,after
,complete
, etc, but they would fire before all containers were queried, and after all drops were complete.I mention this also in #185
Wrap up
I hope that all makes sense.
I think the use cases are clear, and I think I'm presented POC code which hopefully would give you ideas on how to implement in the core.
Would love to chat more about this!
The text was updated successfully, but these errors were encountered: