Alfred-Skyblue / vue-draggable-plus

Universal Drag-and-Drop Component Supporting both Vue 3 and Vue 2
https://vue-draggable-plus.pages.dev/en/
MIT License
2.72k stars 106 forks source link

Implementing Restricted Column Movement for Specific Items #95

Open OliverArthur opened 5 months ago

OliverArthur commented 5 months ago

Hello,

I'm currently working with a Vue component to create a draggable list across multiple columns. My objective is to restrict the movement of certain items between columns based on a specific property (e.g., required property).

In my implementation, items are draggable within and across columns. However, I need to implement a functionality where items marked as required should not be moved to other columns, but can still be rearranged within their own column.

Here is a snippet of my current implementation:

sortable-list.vue

<template>
    <div class="sortable">
        <div v-for="(column, index) in columns" :key="index" class="sortable__list">
            <div class="sortable__list-header">
                <strong :id="`column-header-${index}`">{{ column.name }}</strong>
            </div>
            <vue-draggable
                v-model="column.items"
                :group="groupName || 'default'"
                role="list"
                class="sortable__list-content"
                :direction="dragDirection"
                @move="onMove"
                @end="onEnd"
                @start="onStart"
            >
                <div
                    v-for="item in column.items"
                    :id="item.id"
                    :key="item.id"
                    :aria-grabbed="isGrabbed"
                    class="sortable__list-item"
                    role="listitem"
                    tabindex="0"
                >
                    <i class="c-sortable__list-item__icon" />
                    {{ item.name }}
                    <span v-if="item.required" class="sortable__list-item__required">required</span>
                </div>
            </vue-draggable>
        </div>
    </div>
</template>

<script lang="ts" setup>
    import { ref, computed } from "vue";
    import { VueDraggable } from "vue-draggable-plus";
    import { type SortableEvent, MoveEvent } from "sortablejs";
    import { SortableColumn, SortableItem } from "@/components/common/sortable-list/types";

    type AllowDropEffect = "move" | "none";

    interface Props {
        columns: SortableColumn[];
        groupName?: string;
    }

    const props = defineProps < Props > ();

    const isGrabbed = ref < boolean > (false);
    const dropEffect = ref < AllowDropEffect > ("none");

    // properties for handle drag direction
    const sortableItem = ref < SortableItem | null > (null);
    const dragDirection = computed(() => (sortableItem.value?.required ? "vertical" : "auto"));

    const emits = defineEmits < {
    (event: "on-start", evt: SortableEvent): void;
    (event: "on-end", evt: SortableEvent): void;
    (event: "on-move", evt: MoveEvent): void;
}> ();

    const onStart = (evt: SortableEvent) => {
        isGrabbed.value = true;
        dropEffect.value = "move";

        const grabbedItemId = evt.item.id;
        const grabbedItem = findDraggedItem(grabbedItemId);

        sortableItem.value = grabbedItem;
        emits("on-start", evt);
    };

    const onEnd = (evt: SortableEvent) => {
        if (evt.type === "end") {
            isGrabbed.value = false;
            dropEffect.value = "none";
            sortableItem.value = null;
        }
        emits("on-end", evt);
    };

    const onMove = (evt: MoveEvent) => {
        if (dragDirection.value === "vertical") {
            if (evt.from !== evt.to) {
                return false;
            }
        }
        emits("on-move", evt);
    };

    const findDraggedItem = (itemId: string) => {
        for (let column of props.columns) {
            // find the item with the matching id
            for (let item of column.items) {
                if (item.id === itemId) {
                    return item;
                }
            }
        }
        return null;
    };
</script>

sortable-list-two-column.vue

<template>
    <div>
        <sortable-list class="sortable" :columns="columns" group="myGroup" />
    </div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import sortableList from "@/components/common/sortable-list/sortable-list.vue";
import type { SortableColumn } from "@/components/common/sortable-list/types";

const columns = ref<SortableColumn[]>([
    {
        name: "Column 1",
        items: [
            { id: "1", name: "Item 1" },
            { id: "2", name: "Item 2" },
            { id: "3", name: "Item 3" }
        ]
    },
    {
        name: "Column 2",
        items: [
            { id: "4", name: "Item 4" },
            { id: "5", name: "Item 5" },
            { id: "6", name: "Item 6", required: true }
        ]
    }
]);
</script>

I have attempted to address this issue by making adjustments in the onStart and onMove events, as shown in the provided code. My approach involves controlling the draggable behavior based on the required property of the items. However, these modifications have not been successful

gedrick commented 4 months ago

Hi there @OliverArthur , any luck with this? I've been trying to prevent dragging a group into another group via the @move handler and returning false doesn't seem to prevent the move at all.

OliverArthur commented 4 months ago

Hi @gedrick ,

I gave up in the end after many different tries, and I think this library is not being maintained.

Now I'm using this library @formkit/drag-and-drop, which I discovered recently during Vue.js Amsterdam last week. It's a very good library and allows you to extend the drag-and-drop functionality via custom plugins that you can create.

gedrick commented 3 months ago

Hi @gedrick ,

I gave up in the end after many different tries, and I think this library is not being maintained.

Now I'm using this library @formkit/drag-and-drop, which I discovered recently during Vue.js Amsterdam last week. It's a very good library and allows you to extend the drag-and-drop functionality via custom plugins that you can create.

Thanks for the reply - I did ultimately get it working - I had to call .pause() on the draggable instance - but it was certainly more painful than I expected. And now dealing with what appears to be an abandoned library is very disheartening.

I will check out that one you linked.