I have nested draggable elements in my vue project and for drag event, I am using vue-draggable. So you can imagine my view something similar with this:
--> Element A
--> Element B
-->Element C
--> Element D
--> Element E
-->Element F
I can freely drag one element into another. So I am trying to create a feature when I drag one element into another one, it should give a confirm warning with a modal. When Confirm is clicked, drag should stay how it is but if it is clicked to cancel, it should be reverted. And I am kindly expecting your help in this one. So for this I have ElementsListComponent:
<template>
<div>
<draggable
v-model="newElement"
v-bind="dragOptions"
handle=".handle"
:move="handleMove"
@end="handleEnd"
>
<NestedElement
v-for="(element, index) in newElement"
:key="`${element.id}_${index}`"
:element="element"
@open-element-sidebar="openElementSidebar"
@edit-entity="editEntity"
@delete-entity="successfullDelete"
:is-expanded="isExpanded"
/>
</draggable>
<Modal
v-if="showWarningModal"
@close="showWarningModal = false"
>
<template>
Confirm Modal
</template>
<template #footer>
<div class="buttons">
<button type="button" class="btn btn-primary mr-2" @click="showWarningModal = false">
{{ "Confirm" }}
</button>
<button type="button" class="btn btn-secondary" @click="revertDraggedElement">Cancel</button>
</div>
</template>
</Modal>
</div>
</template>
<script>
export default {
props: {
elements: {
type: Array,
required: true,
default: () => [],
},
isExpanded: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
newElement: [],
dragOptions: {
group: "plan-elements",
animation: 150,
ghostClass: "ghost",
forceFallback: true,
fallbackOnBody: true,
chosenClass: "dragging",
pull: "clone",
},
isLoading: false,
showWarningModal: false,
draggedElement: {},
parentElement: {},
draggedElementInitialState: {},
};
},
methods: {
openElementSidebar(sidebarName, element) {
this.$emit("open-element-sidebar", sidebarName, element);
},
successfullDelete() {
this.$emit("delete-entity");
},
editEntity(id, type) {
this.$emit("edit-entity", id, type);
},
handleMove(event) {
if (event.draggedContext.element) {
this.draggedElement = event.draggedContext.element;
}
// Store the initial state of the dragged element
this.draggedElementInitialState = {
index: event.draggedContext.index,
container: event.draggedContext.container,
element: event.draggedContext.element,
};
if (event.relatedContext.element) {
this.parentElement = event.relatedContext.element;
}
},
handleEnd() {
this.showWarningModal = true;
},
revertDraggedElement() {
// I dont know what to do here
},
},
watch: {
elements: {
handler(newVal) {
this.newElement = newVal;
},
immediate: true,
},
},
};
</script>
and here is my NestedElement component:
<template>
<div>
<Element
:element="element"
@toggle-accordion="toggleAccordion"
@open-element-sidebar="openElementSidebar"
@delete-entity="successfullDelete"
@edit-entity="editEntity"
:has-child="hasChildElement"
:is-expanded="isOpen"
/>
<draggable
v-if="element.childElements && element.childElements.data"
v-model="element.childElements.data"
v-bind="dragOptions"
class="child-elements-container"
handle=".handle"
:move="handleMove"
@end="handleEnd"
>
<NestedElement
v-if="isOpen || (isOpen && isAllOpen)"
v-for="(childElement, index) in element.childElements.data"
:key="`${childElement.id}_${index}`"
:element="childElement"
@open-element-sidebar="openElementSidebar"
@delete-entity="successfullDelete"
@edit-entity="editEntity"
:is-open="isOpen"
:is-expanded="isAllOpen"
/>
</draggable>
<Modal
v-if="showWarningModal"
@close="showWarningModal = false"
>
<template>
Confirm Modal
</template>
<template #footer>
<div class="buttons">
<button type="button" class="btn btn-primary mr-2" @click="showWarningModal = false">
{{ "Confirm" }}
</button>
<button type="button" class="btn btn-secondary" @click="revertDraggedElement">Cancel</button>
</div>
</template>
</Modal>
</div>
</template>
<script>
export default {
name: "NestedElement",
props: {
element: {
required: true,
type: Object,
},
isExpanded: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
dragOptions: {
group: "plan-elements",
animation: 150,
ghostClass: "ghost",
forceFallback: true,
fallbackOnBody: true,
chosenClass: "dragging",
pull: "clone",
revertClone: true,
},
isAllOpen: false,
isOpen: false,
showWarningModal: false,
draggedElement: {},
parentElement: {},
};
},
computed: {
hasChildElement() {
if (this.element.childElements) {
return this.element.childElements.data.length ? true : false;
}
},
},
methods: {
openElementSidebar(sidebarName, element) {
this.$emit("open-element-sidebar", sidebarName, element);
},
successfullDelete() {
this.$emit("delete-entity");
},
editEntity(id, type) {
this.$emit("edit-entity", id, type);
},
toggleAccordion(isOpen) {
this.isOpen = isOpen;
},
handleMove(event) {
if (event.draggedContext.element) {
this.draggedElement = event.draggedContext.element;
}
if (event.relatedContext.element) {
this.parentElement = event.relatedContext.element;
}
},
handleEnd() {
this.showWarningModal = true;
},
},
watch: {
isExpanded: {
handler(newVal) {
this.isAllOpen = newVal;
this.isOpen = newVal;
},
immediate: true,
},
},
};
</script>
I hope I am clear, I think storing initial state is correct but I dont know how to bring the draggable elements into their initial state if it is canceled. Thank you for the helps.