Vue.Draggable icon indicating copy to clipboard operation
Vue.Draggable copied to clipboard

Suggestion: Allow placeholder when target array is empty

Open GregPeden opened this issue 6 years ago • 26 comments

Just an idea... it would be swell to be able to allow placeholder, perhaps provided as a named slot, which is displayed only when the target array is empty. As soon as an item is dragged in to the 'draggable' field, even before released, the placeholder is no longer rendered. This would appear like the first element being added is going to replace the placeholder upon release.

So one can imagine a note like "drop here to add things" with an infographic icon being in place of nothingness. This secondarily resolves the issue of maintaining a minimum height of the HTML element to allow use when empty.

This past issue discusses a similar need: https://github.com/SortableJS/Vue.Draggable/issues/335

GregPeden avatar Oct 16 '18 00:10 GregPeden

In terms of API, using a named slot would totally makes senses. I like this proposal. I will have to take a carefull look to check if this is doable.

David-Desmaisons avatar Oct 16 '18 03:10 David-Desmaisons

It would be convenient to be able to add this placeholder anywhere, not just inside of the target list, and make its disappearance optional, so it also addresses https://github.com/SortableJS/Vue.Draggable/issues/457 more adequately.

ThePendulum avatar Oct 23 '18 15:10 ThePendulum

I would also second this, especially for nested components.

satvikpendem avatar Dec 23 '18 18:12 satvikpendem

Any news on this? @David-Desmaisons has this made it into your roadmap for a future version?

madebycaliper avatar Mar 24 '19 21:03 madebycaliper

@madebycaliper still have no good idea on how to implement such a feature. I should not be abble to deliver it in the short/mid term.

David-Desmaisons avatar Mar 25 '19 00:03 David-Desmaisons

@David-Desmaisons thanks for the reply. it seems like a complex feature!

madebycaliper avatar Mar 25 '19 03:03 madebycaliper

Think out of the box. You have VUE at hand :-)

    <draggable v-model="list" .....>
       <div v-for="elem in list" :key="elem.order">These are your normal sorted elems</div>
       <p v-if="list.length==0" key="4711">This is shown when container is empty</p>
     </draggable>

(Ok you need a dummy key 4711.)

Doogiemuc avatar Mar 26 '19 16:03 Doogiemuc

@dominiczaq Well your sample is exactly an example that IS NOT WORKING, because vue.draggable needs the inner children to map the list

David-Desmaisons avatar Mar 26 '19 17:03 David-Desmaisons

@Doogiemuc yea, I've implemented a working solution a number of times, as I use this component in a bunch of projects and had to figure it out. But I think having something standardized that's built into the component itself would make it easier to implement and more future-proof

madebycaliper avatar Mar 26 '19 17:03 madebycaliper

@madebycaliper what is your work-around?

David-Desmaisons avatar Mar 26 '19 17:03 David-Desmaisons

@David-Desmaisons I have a solution that works with some glitches <draggable class="listgroup" :class="list._id" v-model="listArray" v-bind="dragOptionsCard" @end="funToMove">

listArray: this.cardList,

cardList() { if (!this.list.cards && this.list.cards.length === 0) { this.list.cards = [] } return this.list.cards },

if you have an empty list the function cardList in computed return an empty list for you to enter the cards in to. in css i gave the empty list a min height of 50 px .listgroup { cursor: move; min-height: 50px; }

that fixed most of the bugs for me

itaishopen avatar Mar 31 '19 08:03 itaishopen

I totally agree with madebycaliper: There should be a standard solution in Vue.draggable. My "workaround" is working fine as far as I tested it: https://codepen.io/Doogie/pen/qvwjGE

Doogiemuc avatar Apr 01 '19 11:04 Doogiemuc

@Doogiemuc I agree. Still looking for a reliable solution.

David-Desmaisons avatar Apr 01 '19 23:04 David-Desmaisons

@David-Desmaisons I often use Vuex to fire a DRAG_START and DRAG_END event and then map state and modify the dimensions of the empty Draggable using CSS. I've also tried :

  • a computed property somewhere in the component that recognizes when the list is empty and defaults the min-height to more than 0px
  • applying a min-height style at all times
  • used an::after{} CSS declaration to mock a "drag here" kind of region.

There are oviously lots of custom tactics available, but it would be great if you offered a standardized solution and some supporting logic.

@SirLamer I definitely like the idea of a named slot, as it would be optional and unobtrusive. If the Draggable component could just conditionally show that slot based on items.length, we could pass an element that would extend the list height if it's empty.

madebycaliper avatar Apr 18 '19 19:04 madebycaliper

I have had some success using the header slot like this as the last element inside the draggable component: <h3 slot="header" v-if="report.report_modules.length === 0" class="text-secondary">Drop modules here...</h3>

johnbamlamb avatar Jul 25 '20 05:07 johnbamlamb

I agree that a named slot would be ideal.

However, here's my v simple workaround that's bug free (for me) :

.list-group:empty {
    padding:1rem;
    text-align:center;
}

.list-group:empty:before {
    content: 'Drop files here';
}

This works with

<draggable class="list-group" etc...

MemeDeveloper avatar Aug 06 '20 16:08 MemeDeveloper

N.B. above doesn't seem to work when using a transition-group as this adds a span to the "empty" list, meaning the :empty selector doesn't get applied.

MemeDeveloper avatar Aug 06 '20 16:08 MemeDeveloper

I got transition-group working with modified css selector and tag="div" as per :

<transition-group type="transition" tag="div">

.list-group:empty,
.list-group > div:empty {
    padding:1rem;
    text-align:center;
}

.list-group:empty:before,
.list-group > div:empty:before {
    content: 'Drop files here';
}

i.e. add a second selector to target the empty div produced by the transition-group

Hope that helps someone out, and thanks for the awesome code !

MemeDeveloper avatar Aug 06 '20 16:08 MemeDeveloper

Any updates?

CheshireCaat avatar Dec 12 '21 23:12 CheshireCaat

@David-Desmaisons this issue was first raised more than 3 years ago. Is it possible to see if this can be implemented in the Vue 3 version?

It doesn't seem like an overly complicated problem to generate/create a cloned copy of the item that is being dragged and have it stay in the original position in the list.

A cloned (ghost) version is already being created, but it's being moved around depending on the mouse position. So another non-moving clone can be created to remain in place where the original item used to sit.

This will make sorting items in the list much calmer to look at as there won't be so much movement happening during the sorting of the item, and it would also fix the webkit hover bug which applies the hover class onto items that are positioned instead of the original item that's being dragged.

adamreisnz avatar Dec 18 '21 05:12 adamreisnz

+1 for this. This is the feature am working on where i want to have a bigger drag area

https://user-images.githubusercontent.com/183790/147861811-b5d872b8-d5fa-4b6b-948d-821a3da290d5.mov

simonj avatar Jan 01 '22 23:01 simonj

I found a workaround for this webkit browsers bug. For every draggable element you need to add mouseover events which take setHoverEffect function.

setHoverEffect (event) {
  const childList = [].slice.call(event.currentTarget.parentElement.children)

  for (const child of childList) {
    child.classList.remove('hovered')
  }

  if( !childList.some((child) => child.classList.contains('sortable-chosen')) ) {
    event.currentTarget.classList.add('hovered')
  }
}
.hovered {
  background-color: orange;
  color: #fff;
}

Then you need to add mouseleave event which will remove hovered class. event.currentTarget.classList.remove('hovered')

artemryskal avatar May 19 '22 21:05 artemryskal

is this project still active? I see no resolution for this issue, so I'm assuming no?

CultivateCreate avatar Feb 09 '23 20:02 CultivateCreate

I found a CSS solution: Add a dummy element in header (or Footer) element with class=hideable-header. Add CSS: .hideable-header:not(:only-child){ display: none; } .hideable-header:only-child{ display: block; } This will result in the element only being shown if it is the only-child of its parent.

In case you have a header or footer already use :first-child or :last-child.

heavy-matill avatar Jun 05 '23 08:06 heavy-matill

Any updates?

Gyurmatag avatar Aug 21 '23 10:08 Gyurmatag

How about doing this.

<Draggable class="list-group" :list="list" group="blocks" item-key="uuid">
  <template #header v-if="draft.length == 0">
    What you want to show when the list is empty
    </template>
  <template #item="{ element, index }">
    Your list
</template>
</Draggable>

This essentially hides the header slot when the list has more than 0 elements.

abejordan avatar Mar 20 '24 16:03 abejordan