vue-grid-layout icon indicating copy to clipboard operation
vue-grid-layout copied to clipboard

Newbie question: Can I drag an object and drop another?

Open nilopaim opened this issue 4 years ago • 2 comments

I have an image as drag origin, but I want drop a diferent object (a Vue component, in fact) on the target. Obviously this component is someway linked to the origin image (the image of a button will drop a real button on target, for example).

Any hints about this?

nilopaim avatar May 05 '21 11:05 nilopaim

I do something similar. There might be a better way of achieving it, but this what I do:

Based on the 'drag from outside' example 10 I define my layout:

layout: [
  { x: 0, y: 0, w: 1, h: 1, i: "0", kind: "button"},
  { x: 2, y: 0, w: 2, h: 4, i: "1", kind: "textarea" },
],

and define a palette of things to drag. In my case I am dragging out form controls.

    <div class="palette">
      <div @drag="drag" @dragend="dragend" class="droppable-element" draggable="true"
            unselectable="on">Textarea<textarea disabled></textarea></div>
      <div @drag="drag" @dragend="dragend" class="droppable-element" draggable="true"
            unselectable="on"><button>Click Me</button></div>
      <div @drag="drag" @dragend="dragend" class="droppable-element" draggable="true"
            unselectable="on"><input disabled></div>
    </div>

Based on the dragged component's nodeName e.g. input or button I set the kind property of TestElement

        <grid-item
          v-for="item in layout"
          :x="item.x"
          :y="item.y"
          :w="item.w"
          :h="item.h"
          :i="item.i"
          :key="item.i"
        >
          <TestElement :msg="item.i" :kind="item.kind"></TestElement>
          
        </grid-item>

Notice I am creating a bunch of TestElement components which are wrappers around the real component. Within the TestElement, as mentioned in #56 is a bunch of v-if which creates the needed component based on kind.

<template>
  <div class="wrapper">
    <button v-if="kind == 'button'">Click me</button>

    <input v-if="kind == 'input'" />

    <textarea v-if="kind == 'textarea'"></textarea>

    <div v-if="kind == 'select'">
      <label for="cars">Choose a car:</label>
      <select id="cars" name="cars">
        <option value="volvo">Volvo</option>
        <option value="saab">Saab</option>
        <option value="fiat">Fiat</option>
        <option value="audi">Audi</option>
      </select>
    </div>

    <div v-if="kind == ''">
      {{ msg }}
    </div>

  </div>
</template>

abulka avatar Aug 18 '21 07:08 abulka

It turns out that the other, better way of doing it (also mentioned in #56) is to use the vue built in dynamic component creation trick, which creates a component specified by the value of :is="someComponentName".

Here you would define your grid data like this, where kind refers to a vue component name (notice it starts with an uppercase letter):

layout: [
  { x: 6, y: 0, w: 1, h: 1, i: "2", kind: "Button", },
  { x: 7, y: 0, w: 1, h: 1, i: "3", kind: "ButtonHugging"  },
  { x: 6, y: 2, w: 1, h: 1, i: "4", kind: "Select" },

Your template would look like this:

      <grid-layout
        ref="gridlayout"
       ...
      >
        <grid-item
          v-for="item in layout"
          :x="item.x"
          :y="item.y"
          :w="item.w"
          :h="item.h"
          :i="item.i"
          :key="item.i"
        >
          <component v-bind:is="item.kind" :msg="item.i"></component>

        </grid-item>
      </grid-layout>

Notice we are no longer using a wrapper component TestElement but specifying the component we want directly via item.kind. In the for loop, the component that ends up being created within the grid-item by vue may be different each time.

Hooking into drag/drop

Define a palette of things to drag

    <div class="palette">
      <div @drag="drag" @dragend="dragend" class="droppable-element" draggable="true"
            unselectable="on" data-kind="Button">
            <Button>Drag Button</Button>
      </div>
      ...
    </div>

the component you want to create on drop is stored in the attribute data-kind. At least that's how I did it.

Assuming you are using the drag/dragend functions from example 10, you'll have to modify the drag function to pass the kind information all the way through to the dragend function where the final this.layout.push(item) happens. The item you push needs to have the kind property for the vue template to do its thing.

Passing the kind information from the dragged palette object involves setting kind on the temporary 'drop' item, then on the global DragPos object, then on the final item that is pushed by the dragend function.

abulka avatar Aug 20 '21 03:08 abulka