4

I've been banging my head against the wall trying to figure this out. I found an example of pretty much exactly what I'm trying to do via the Sortable.js library repo here but I haven't been able to implement it using Vue.Draggable.

My theory of how to do it is add a <draggable/> element that is in the same group as the rest of the elements and listen for an @add event on it, then remove that dragged element from its list.

One gotcha (maybe) is I'm trying to designate a "trash" button as the removal region, and if it references the same draggable group, that dragged element gets appended to the button group and throws off the whole UI. So, a really important thing as that there's some sort of user feedback (e.g. trash icon turns red when hovering with drag element), but the dragged element shouldn't get inserted to the button group.

Initial Drag State

mouse cursor over the item to delete

Intended Delete Action

enter image description here

Thanks in advance!

slowFooMovement
  • 498
  • 5
  • 14

1 Answers1

8

Lets solve some of the problems in turn:

How do I let a trash draggable accept any group

The answer to that is to set the pull and push of the group attribute.

  trashOptions: {
    group: {
      name: 'trash',
      draggable: '.dropitem',
      put: () => true,
      pull: false
    }
  }

By setting put to a function that always returns true, it will accept any group. Alternatively you can set it to an array with groups you accept from.

How do I prevent my button from being messed up when setting it as a draggable area

The easiest way is to set the inserted item to display: none. You can simply hide whatever you set as the draggable attribute, or use the ghostClass setting (as per the documentation).

How do I give the user feedback?

This one is tricky. Draggable does not set a class on the draggable area you are using. You can however use a smart css selector to sort-of achieve what you want. First we define a footer.

<draggable v-model="trashZone" class="dropzone trashzone" :options="trashOptions">
  <div slot="footer" class="footer">Trash</div>
</draggable>

We position this absolute to the dropzone and make sure it covers the entire thing. Dropped elements will now always appear before the footer. This is great, because we can use the next-selector (+) to select the footer after the inserted dropitem, and apply different styling. In this case we apply a red background and make the text white.

.trashzone .footer {
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.trashzone .dropitem + .footer {
  background: red;
  color: white;
}

A full working example is available on codesandbox.

Edit Vue Template

Sumurai8
  • 20,333
  • 11
  • 66
  • 100
  • this is super helpful! Two things aren't quite right tho – 1) may be ddue to Vue rerendering, but when an element is dragged to the trashzone (my trash button) the parent element loses its hover state and the button group disappears. Could be CSS...I'll keep investigating 2) I don't think I can use the absolute pos background for user feedback. I'd prefer to have some sort of class toggle on the button and just change the bg color. I was thinking about trying to leverage the `@start` or `@change` events. Any advice on how you'd tackle that? – slowFooMovement Jul 31 '18 at 21:21
  • You could detect when you have an item on the cursor by setting a dummy variable `dragging` to `true` on `@start` and to `false` on `@end`, then possibly detect on hover on the trash draggable. The thing is, that will probably fail with touch events. I think most (if not all) of the Draggable events fire when you drop an item, which is too late if you want to have visual feedback. – Sumurai8 Aug 01 '18 at 07:18
  • yea I actually ended up implementing it almost exactly how you suggested (using `$emit` I can creat conditional classes and modify the appearance via CSS) but I couldn't figure out the `:hover` portion of it. I'll follow up if I can make some headway. thanks for the help! – slowFooMovement Aug 01 '18 at 22:24