Skip to content
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

onDragend should be emitted from the target instead of source #117

Open
carlosvaldesweb opened this issue Nov 26, 2024 · 2 comments
Open
Assignees

Comments

@carlosvaldesweb
Copy link

carlosvaldesweb commented Nov 26, 2024

Hello, if you have multiple nested groups, i think that onDragend should be emitted by the component where you're letting go the element.

Imagine that i have a Scholar Groups and Courses, and i can move courses between groups.

I have CoursesList.vue component and GroupsList.vue component, how can i send group_id params for example when i move courses between groups in my api calls? I've tried using onsort and onTransfer, but i dont want to make apiCalls every movement, if not until the user drops the element.

GroupList.vue

<script lang="ts" setup>
import { dragAndDrop } from "@formkit/drag-and-drop/vue";

const sanctumClient = useSanctumClient();

const groups = defineModel({
  type: Array as PropType<Array<Group>>,
  required: true,
});
const groupsRef = ref<any>();
const toast = useToast();
const state = ref({
  position: 0,
  group_id: 0,
});

dragAndDrop({
  parent: groupsRef,
  values: groups,
  group: "groups",
  onDragend: (event) => {
    const group = groups.value[event.draggedNode.data.index];
    const nextGroup = groups.value[event.draggedNode.data.index + 1];
    const previousGroup = groups.value[event.draggedNode.data.index - 1];

    state.value.group_id = group.id;
    state.value.position = group.position;

    if (previousGroup && nextGroup) {
      state.value.position =
        ((previousGroup.position as number) + (nextGroup.position as number)) /
        2;
    } else if (previousGroup) {
      state.value.position =
        (previousGroup.position as number) +
        (previousGroup.position as number) / 2;
    } else if (nextGroup) {
      state.value.position = (nextGroup.position as number) / 2;
    }

    moveGroup();
  },
});
const onCourseCreated = (groupId: number) => {
  const groupsRef = document.getElementById("group-" + groupId);
  groupsRef!.scrollTop = groupsRef!.scrollHeight;
};

const { submit: moveGroup } = useSubmit(
  () =>
    sanctumClient("/groups/" + state.value.group_id + "/move", {
      method: "PUT",
      body: {
        position: state.value.position,
      },
    }),
  {
    onSuccess: () => {
      groups.value!.forEach((group) => {
        if (group.id === state.value.group_id) {
          group.position = state.value.position;
        }
      });
    },
    onError: () => {
      refreshNuxtData("groups");
      toast.add({
        title: "Error",
        description: "Error moving group",
        icon: "i-ph-x",
        color: "red",
      });
    },
  },
);
</script>

<template>
  <div class="inline-flex space-x-4 max-h-full">
    <div
      ref="groupsRef"
      v-auto-animate
      class="inline-flex space-x-4 max-h-full"
    >
      <div
        v-for="group in groups"
        :id="'group-' + group.id"
        :key="group.id"
        class="w-72 bg-gray-200 dark:bg-gray-800 rounded-lg max-h-full flex flex-col"
        :group-id="group.id"
      >
        <div class="px-4 py-2 flex justify-between gap-2">
          <GroupsUpdateGroupName :group="group" />
          <GroupsDeleteGroup :group="group" />
        </div>
        <CoursesList v-model="group.courses" :group="group" />
        <div class="px-4 py-2">
          <CoursesCreateCourseButton
            :group="group"
            @course-created="onCourseCreated"
          />
        </div>
      </div>
    </div>
    <GroupsCreateGroupButton />
  </div>
</template>

<style></style>

CourseList.vue

<script lang="ts" setup>
import { dragAndDrop } from "@formkit/drag-and-drop/vue";
import { useDebounceFn } from "@vueuse/shared";

const props = defineProps({
  group: {
    type: Object as PropType<Group>,
    required: true,
  },
});
const courses = defineModel({
  type: Array as PropType<Array<Course>>,
  required: true,
});
const coursesRef = ref<any>();
const state = ref({
  position: 0,
  group_id: 0,
  course_id: 0,
});

dragAndDrop({
  parent: coursesRef,
  values: courses,
  group: "courses",
  onDragend: (event) => {
    console.log("entrando en dragend desde " + props.group.id);
    return;
    const course = courses.value[event.draggedNode.data.index];
    state.value.course_id = course.id;
    const nextCourse = courses.value[event.draggedNode.data.index + 1];
    const previousCourse = courses.value[event.draggedNode.data.index - 1];

    state.value.group_id = course.id;
    state.value.position = course.position;

    if (previousCourse && nextCourse) {
      state.value.position =
        ((previousCourse.position as number) +
          (nextCourse.position as number)) /
        2;
    } else if (previousCourse) {
      state.value.position =
        (previousCourse.position as number) +
        (previousCourse.position as number) / 2;
    } else if (nextCourse) {
      state.value.position = (nextCourse.position as number) / 2;
    }

    moveCourse();
  },
  onSort: (event) => {
    console.log("entrando en sort desde " + props.group.id);
  },
  onTransfer: (event) => {
    console.log("entrando en transfer desde " + props.group.id);
    console.log(event);
  },
});

const onMove = useDebounceFn(() => {
  const course = courses.value[event.draggedNode.data.index];
}, 1000);

const sanctumClient = useSanctumClient();
const toast = useToast();
const { submit: moveCourse } = useSubmit(
  () =>
    sanctumClient("/courses/" + state.value.course_id + "/move", {
      method: "PUT",
      body: {
        position: state.value.position,
      },
    }),
  {
    onSuccess: () => {
      // groups.value!.forEach((group) => {
      //   if (group.id === state.value.group_id) {
      //     group.position = state.value.position;
      //   }
      // });
    },
    onError: () => {
      refreshNuxtData("groups");
      toast.add({
        title: "Error",
        description: "Error moving group",
        icon: "i-ph-x",
        color: "red",
      });
    },
  },
);
</script>

<template>
  <ul
    ref="coursesRef"
    v-auto-animate
    class="flex flex-col flex-1 overflow-y-auto overflow-x-hidden px-4 space-y-2"
  >
    {{
      group.id
    }}
    <li
      v-for="course in courses"
      :key="course.id"
      class="bg-white shadow dark:bg-gray-700 rounded-lg p-4 hover:shadow-lg transition-all flex"
    >
      <CoursesUpdateCourseName :course="course" />
      <CoursesDeleteCourseButton :course="course" :group="group" />
    </li>
  </ul>
</template>

<style></style>
@sashamilenkovic
Copy link
Contributor

Great question. I think handleEnd should be invoked for both the parent in which it was dropped as well as where it began from (the source parent). I'm working on a big release and I'd like to add this in there. Do you think that would satisfy your use case?

@sashamilenkovic
Copy link
Contributor

Btw, I'm curious, we have an experimental insert plugin on the docs. Would that implementation serve you better for nested groups?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants