Coder Social home page Coder Social logo

gilnd / vue3-smooth-dnd Goto Github PK

View Code? Open in Web Editor NEW
178.0 1.0 16.0 270 KB

Vue3 wrapper components for smooth-dnd

Home Page: https://gilnd.github.io/vue3-smooth-dnd/

License: MIT License

JavaScript 45.73% HTML 2.03% Vue 48.38% CSS 3.86%
vue vue3 dnd smooth-dnd draggable drag-and-drop

vue3-smooth-dnd's Introduction

vue3-smooth-dnd

Vue 3 Wrapper of smooth-dnd library.
Live demo

NOTE: This is a Vue 3 wrapper over smooth-dnd library. It's a fork of the already done vue2 wrapper done by the original author of the library.

All the documentation for the Vue 2 version works with this package version too!

Install

yarn add vue3-smooth-dnd

Usage

<template>
  <div>
    <span>Studio Ghibli Tier List</span>
    <Container orientation="vertical" @drop="onDrop">            
      <Draggable v-for="(item, i) in items" :key="item.id">
        <div>
           {{i + 1}} -> {{item.data}}
        </div>
      </Draggable>
    </Container>
  </div>
</template>

<script>
import { Container, Draggable } from "vue3-smooth-dnd";
export default {
  name: "app",
  components: { Container, Draggable },
  data() {
    return {
      items: [
        { id: 1, data: "Princess Mononoke" },
        { id: 2, data: "Spirited Away" },
        { id: 3, data: "My Neighbor Totoro" },
        { id: 4, data: "Howl's Moving Castle" }
      ]
    };
  },
  methods: {  
    onDrop(dropResult){
      this.items = this.applyDrag(this.items, dropResult);
    },
    applyDrag(arr, dragResult){
      const { removedIndex, addedIndex, payload } = dragResult;

      if (removedIndex === null && addedIndex === null) return arr;
      const result = [...arr];
      let itemToAdd = payload;
      
      if (removedIndex !== null) {
        itemToAdd = result.splice(removedIndex, 1)[0];
      }
      if (addedIndex !== null) {
        result.splice(addedIndex, 0, itemToAdd);
      }
      return result;
    }
  }
}
</script>

vue3-smooth-dnd's People

Contributors

ffxsam avatar gilnd avatar stafyniaksacha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

vue3-smooth-dnd's Issues

Items dropped outside container perform "removeOnDropOut" animation even if that option is false

Reproduction here:
https://stackblitz.com/edit/vue-udpcdk?file=src%2FApp.vue,src%2Fcomponents%2FBrokenList.vue,src%2Fcomponents%2FWorkingList.vue

https://stackblitz.com/edit/vue-udpcdk?file=src%2FApp.vue,src%2Fcomponents%2FBrokenList.vue,src%2Fcomponents%2FWorkingList.vue

Top list is vue3-smooth-dnd, bottom list is regular smooth-dnd. Note that if you drag an item outside the top list, it acts like it's going to remove itself. This doesn't happen in smooth-dnd, so this is somehow an issue limited to this fork?

Happy to help fix this if you have a hunch. This package just seems like a wrapper around smooth-dnd, and I don't see any reason it should behave this way.

CleanShot.2023-04-20.at.12.43.29.mp4

Do not use filters

Just found out that adding a filter such as this one:
filter: drop-shadow(9px 15px 8px rob(0 0 0 / 0.35)) ;

This results in the ghost being offset from the target element.
Same effect as this issue:
kutlugsahin/vue-smooth-dnd#31

Happy coding.

No event provided on "drag-enter"

when using @drag-enter on a Container element, absolutely no event or information on which element you're dragging over is provided. This is problematic as it's then not possible to do any styling on that container.
basically this(not full code):
<Container @drag-enter="onDragEvent($event)"> <--- does not work, in console log, the "$event" is undefined.
How do we get the event with target and source element data and all that? Is this a bug?

Screen scroll does not work on mobile

When I am using drag n drop on mobile I am prevented from touchmoving the list...I attach video

IMG_2423.1.mp4

Could it be for this css class of yours?

.smooth-dnd-disable-touch-action * {
  touch-action: hidden;
  display: block;
}

View not scrolling when dragged card near edges

See video using demo in Chrome.

Dragged card near edges doesn't scroll the view horizontally.
Occurs when browser is not full-width, or when div is not full width.

Scrolling works on column headers.

Screen.Recording.2022-02-18.at.12.25.02.AM.2.mov

Incorrect Evaluation of 'addedIndex'

I am encountering an issue while utilizing this drag and drop library in my Vue demo project. I have observed that the 'addedIndex' parameter is evaluating incorrectly in certain cases.

Here is the code snippet

<template>
<div class="flex-container">
  <div class="container">
    <Container
      behaviour="copy"
      group-name="1"           
      :get-child-payload="getChildPayload"
      tag='div'
      orientation="vertical"
      drag-handle-selector="div"
      class="inner-container"
    >
      <Draggable
      v-for="(item, idx) in items"
      :key="idx"
      tag='div'      
      >
          <div class="item">
            {{item.text}}
          </div>
      </Draggable>
    </Container>
  </div>

  <div class="container" >
    <Container
      behaviour="contain"
      group-name="1"
      @drop="onDrop($event)"
      tag='div'
      drag-handle-selector="div"
      orientation="vertical"
      class="inner-container"
    >
      <Draggable
      v-for="(item, idx) in items2"
      :key="idx"
      :tag="{ value: 'div' }"
      >
        <div class="item">
          <span>
            {{item.text}}                     
          </span>
          <button
            @click.stop="removeItem(idx)"
            style="padding: 8px;"
            >
             delete
          </button>
        </div>
      </Draggable>
    </Container>
  </div>
</div>
</template>

<script setup>
import { Draggable, Container } from 'vue3-smooth-dnd';
import { ref } from 'vue'

const items = ref([
  {
    id: 1,
    text: `Item 1`,
  },
  {
    id: 2,
    text: `Item 2`,
  },
  {
    id: 3,
    text: `Item 3`,
  },
  {
    id: 4,
    text: `Item 4`,
  },

])

const items2 = ref([]);

const getChildPayload = (index) => {
  const item = items.value[index];
  let payload = {
    id: item.id,
    text: `${item.text} on container 2`,
  }

  return payload;
}

const onDrop = (dragResult) => {
  const { removedIndex, addedIndex, payload } = dragResult
  console.log('Added index = ', addedIndex);// for logging the addedIndex
  if (removedIndex === null && addedIndex === null) return items2.value

  const result = [...items2.value]
  let itemToAdd = payload

  if (removedIndex !== null) {
    itemToAdd = result.splice(removedIndex, 1)[0]
  }

  if (addedIndex !== null) {
      result.splice(addedIndex, 0, itemToAdd)

  }
  items2.value = result;
}

const removeItem = (index) => {
  items2.value.splice(index, 1)
}
</script>

<style scoped>
.flex-container{
  display: flex;
}
.container{
  background-color: grey;
  width: 600px;
  height: 600px;
  margin:auto 16px;
}

.inner-container{
  background-color: red;
}

.item{
  margin:4px;
  padding: 8px;
  background-color: white;
  text-align: center;
}

</style>

Steps to Reproduce:

  • Drag more than two elements from container 1 to container 2.

  • Observe the logged 'addedIndex', initial behavior is as expected .

  • 01

  • Delete all items from container 2.

  • 02

  • Add one element from container 1 to container 2 again.

  • Notice that the 'addedIndex' logged in the console is being recorded as '1' instead of '0' for the first element.

  • 03

  • Additionally, the 'addedIndex' is observed as '2' for the first element if more than four items (i think) were initially added, deleted, and then re-added.

Plugin does not work with Iframes

Hello,

the plugin does not harmonize with iframes. When you move an item over an iframe, it starts to hang. It should be better that items are also dragged into the iframe and can be edited in it.

If I display the items in the iframe itself, it is somehow as if the items no longer have events.

<Container :key="size" 
  orientation="vertical" 
  group-name="col-items"  
  :drop-placeholder="{ className: 'holder', animationDuration: '200', showOnTop: true }"
  drag-class="test"
  drop-class="drop">
  <template v-for="item in 8" :key="item">
    <Draggable v-if="item">
      <div class="pa-0">
        <div class="pa-2 bg-white tc-dark">
          {{ item }}
        </div>
      </div>
    </Draggable>
  </template>
</Container>

Drop doesn't animate like original smooth-dnd library

I noticed when I drop something a little far off from the proper destination, it just snaps right away, whereas with smooth-dnd, it animates as it drops into place.

Is there some way to get that working?

CleanShot.2023-02-17.at.15.03.40.mp4

Dragging Up Works But Dragging Down Doesn't - Attaching Video and Code

Hi, So I'm using this for the first time.

I'm using vue 3 and vue 3 DnD package

I'm trying to implement this on a form builder (think Microsoft forms).

I'm having a problem where I can drag up, and it'll work, but when I drag down it won't work

Here is a video of the problem

This is my HTML code (Removed some repetitive code as the block was getting longer)

<!-- Sections -->
<div
  v-for="(_, sectionIndex) in formStructure"
  :key="`section-${sectionIndex}`"
  class="mb-8 bg-base-200 border border-primary-focus rounded-md py-4"
>
  <h3
    class="text-3xl mb-8 px-2 md:px-6 underline decoration-primary-focus underline-offset-8 opacity-70"
  >
    Section #{{ sectionIndex + 1 }}
  </h3>
  <Container
    group-name="1"
    @drop="onDrop(sectionIndex, $event)"
    :get-child-payload="getChildPayload(sectionIndex)"
    drag-class="bg-accent"
    lock-axis="y"
    @drag-end="null"
    class="h-full bg-black"
  >
    <!-- Questions -->
    <Draggable
      v-for="(question, index) in formStructure[sectionIndex]"
      class="px-2 md:px-6 pb-8 pt-4 mb-4 text-2xl border-b border-primary border-opacity-40"
      :key="`question-${index}`"
      drag-handle-selector="drag-handle"
    >
      <!-- Drag Icon / Anchor -->
      <div
        class="w-full flex justify-center hover:text-primary text-base-content cursor-grab"
      >
        <i-ci-drag-horizontal @mousedown.left="null" />
      </div>
      <!-- Question -->
      <QuestionStatement
        v-model="question.statement"
        :index="index"
        :question="question"
        @deleteQuestion="deleteQuestion(index, sectionIndex)"
      />
      <!-- Question Content -->
      <div v-if="!reorder">
        <!-- Choice Question -->
        <div
          v-if="question.type == 'single' || question.type == 'multiple'"
          class="w-full"
        >
          <div class="mt-4">
            <ChoiceOptions
              :index="index"
              :optionIndex="optionIndex"
              :type="question.type"
              @deleteOption="(question as ChoiceClass).deleteOption(optionIndex)"
              v-for="(option, optionIndex) in (question as ChoiceClass).options"
              v-model:value="option.value"
              v-model:score="option.score"
              :key="`question-${index}-option-${optionIndex}`"
            />
            <!-- Place Holder Only -->
            <CommentAndImage :question="question" />
          </div>
          <!-- Actions -->
          <div
            class="grid grid-cols-2 lg:flex lg:items-end lg:justify-end gap-4 mt-8"
          >
            <CommentAndImageButtons :question="question" />
            <button
              class="btn btn-sm btn-primary btn-outline"
              @click="(question as ChoiceClass).addOption()"
            >
              Add Option
            </button>
            <button
              class="btn btn-sm btn-primary btn-outline"
              @click="(question as ChoiceClass).addOtherOption()"
            >
              Add 'Other' Option
            </button>
          </div>
        ...
        </div>
      </div>
    </Draggable>
  </Container>
</div>

This is my JS code

...
import { Container, Draggable } from "vue3-smooth-dnd";
...
...

const showForm = ref(true);
const reorder = ref(false);
const getChildPayload = function (sectionIndex: number) {
  return (index: number) => {
    return formStructure[sectionIndex][index];
  };
};
const onDrop = (sectionIndex: number, { removedIndex, payload, addedIndex }) => {
  console.log(removedIndex, addedIndex);  
  showForm.value = false;
  if (removedIndex === null && addedIndex === null) return;
  const result = [...formStructure[sectionIndex]];
  if (removedIndex !== null) {
    result.splice(removedIndex, 1)[0];
  }

  if (addedIndex !== null) {
    result.splice(addedIndex, 0, payload);
  }
  formStructure[sectionIndex] = result;
  showForm.value = true;
};

This is my data object

[
  [
    {
      "position": 2,
      "type": "matrix",
      "statement": "Question 2",
      "comment": false,
      "image": false,
      "subQuestions": [
        {
          "statement": "Please Type Your Question",
          "comment": false
        },
        {
          "statement": "Please Type Your Question",
          "comment": false
        }
      ],
      "optionsCount": 5,
      "options": [
        {
          "value": "Option"
        },
        {
          "value": "Option"
        },
        {
          "value": "Option"
        },
        {
          "value": "Option"
        },
        {
          "value": "Option"
        }
      ]
    },
    {
      "position": 1,
      "type": "multiple",
      "statement": "Question 1",
      "comment": false,
      "image": false,
      "options": [
        {
          "value": "Option 1",
          "score": "1"
        },
        {
          "value": "Option 2",
          "score": "2"
        }
      ]
    }
  ]
]

As I was trying this package for the first time... I made a trial, and that worked perfectly (It's got the same JS logic)

<script setup>
import { Container, Draggable } from "vue3-smooth-dnd";
import { reactive } from "vue";

let items = reactive([
 [
   "item 0-0",
   "item 0-1",
   "item 0-2",
 ],
 [
   "item 1-0",
   "item 1-1",
   "item 1-2",
 ],
]);
const getChildPayload = function (listIndex) {
 return (index) => {
   return items[listIndex][index];
 };
};
const onDrop = (listIndex, { removedIndex, payload, addedIndex }) => {
 if (removedIndex === null && addedIndex === null) return;
 const result = [...items[listIndex]];
 if (removedIndex !== null) {
   result.splice(removedIndex, 1)[0];
 }

 if (addedIndex !== null) {
   result.splice(addedIndex, 0, payload)
 }
 items[listIndex]=result;
};


</script>

<!-- Your Markup Here -->
<template>
 <div>
   <div class="flex justify-center gap-x-12">
     <div
       v-for="(item, listIndex) in items"
       :key="`section-${listIndex}`"
       class="border border-primary mx-8"
     >
       <Container
         group-name="1"
         @drop="onDrop(listIndex, $event)"
         :get-child-payload="getChildPayload(listIndex)"
       >
         <Draggable
           v-for="(subItem, subIndex) in item"
           :key="`subItem-${subIndex}`"
           class="px-4 border-b border-primary-focus last-of-type:border-0 py-4"
         >
           <div>{{ subItem }}</div>
         </Draggable>
       </Container>
     </div>
   </div>
 </div>
</template>

Sorry for the long post, but I'm at the end of my wits trying to figure this out

Thanks in advance

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.