SortableJS / Vue.Draggable

Vue drag-and-drop component based on Sortable.js
https://sortablejs.github.io/Vue.Draggable/
MIT License
20.1k stars 2.9k forks source link

Recursive rendering weird dragging behavior for nested elements #1055

Open Celtech opened 3 years ago

Celtech commented 3 years ago

EDIT: One more layer to this issue I noticed, The lowest level children when I try to drag it somewhere else it doesn't work, acts like it will but goes back where it was (i.e it's children are empty). But when I drag the parent of a child somewhere else it does one of the following:

  1. Moves the parent and it's parent somewhere else where I drag it, but leaves the child where it was i.e image Initial state on a refresh

image After I drag col-2 to container-1

or

  1. Moves the child into the container where I dragged the parent to but leaves the parent where it was i.e image Initial state on a refresh

image After dragging col-2 into col-1

Original Post

Hey guys, been building a landing page builder and decided vue-draggable would be a nice addition. That said after 3 days of headaches trying to make this work I'm at a loss. So far I've followed the nested example guide which has been KINDA working, in addition I followed an issue about the nested guide on here adding an emitter to the children for proper updates. Now my getters and setters are firing BUT I'm still having a problem dragging elements(see the video)

http://www.giphy.com/gifs/ZMiyi8LEcI73nye1ZN

As you can see when I drag stuff around it's strange behavior:

Example cases: When I drag col 2 label into col 1 it moves the children inside into col one, does not change col 2s place When I drag paragraph label anywhere it will not move, shows like it will but when I release nothing happens If I drag row 1 from the original starting state you saw in the gif into the paragraph I end up with the following: image

Just 3 sample cases, references: https://sortablejs.github.io/Vue.Draggable/#/nested-with-vmodel https://github.com/SortableJS/Vue.Draggable/issues/701#issuecomment-686187071

my code creating these results:

component-renderer.vue (THERE'S A NOTE IN HERE TO READ)

  <draggable
    v-bind="dragOptions"
    :list="list"
    :value="value"
    style="position: relative; border: 1px solid red"
    :tag="data.tagName"
    :class="data.attributes.class + ` border border-danger p-3`"
    @input="emitter"
    @change="onChange" //NOTE: I've tried setting the group here to row instead of in the computed prop below, didn't work
  >
    <slot></slot>

    <component-renderer
      v-for="el in realValue"
      :key="el.attributes.id"
      :list="el.children"
      :data="el"
      :child="true"
      @change="onChange"
    >
      <span style="position: absolute; top: 0; left: 0; background: red">{{
        `${el.tagName} - ${el.attributes.id}`
      }}</span>

      {{ el.textNode }}
    </component-renderer>
  </draggable>
</template>

<script>
import draggable from "vuedraggable";

export default {
  name: "ComponentRenderer",
  components: {
    draggable,
  },
  props: {
    data: {
      required: false,
      type: Object,
      default: null,
    },
    value: {
      required: false,
      type: Array,
      default: null,
    },
    list: {
      required: false,
      type: Array,
      default: null,
    },
    child: {
      type: Boolean,
      default: false,
      required: false,
    },
  },
  computed: {
    dragOptions() {
      return {
        animation: 0,
        disabled: false,
        ghostClass: "row",
        group: "row",
      };
    },
    realValue() {
      return this.value ? this.value : this.list;
    },
  },
  methods: {
    emitter(value) {
      this.$emit("input", value);
    },
    onChange: function () {
      if (this.child === true) {
        this.$emit("change");
      } else {
        this.emitter(this.value);
      }
    },
  },
};
</script>

<style scoped></style>

PageEditor.vue:

  <div id="wysiwyg-page-editor">
    <ChargeOverNavBar />
    <div class="editor">
      <ComponentRenderer v-model="elements" :data="elements[0]" />
    </div>
    <ChargeOverFooter />
  </div>
</template>

<script>
import ChargeOverNavBar from "@/components/ChargeOverNavBar";
import ChargeOverFooter from "@/components/ChargeOverFooter";
import InlineEditor from "@ckeditor/ckeditor5-build-inline";
import ComponentRenderer from "@/components/module-editor/ComponentRenderer";

export default {
  name: "PageEditor",
  components: {
    ComponentRenderer,
    ChargeOverFooter,
    ChargeOverNavBar,
  },
  data() {
    return {
      editor: InlineEditor,
      editorConfig: {},

      activeSection: null,

      page: {},

      panels: {
        pageProperties: true,
        seoProperties: true,
        sectionProperties: false,
      },
    };
  },
  computed: {
    elements: {
      get() {
        console.log("getter");

        return JSON.parse(JSON.stringify(this.$store.state.editor.editorData));
      },
      set(value) {
        console.log("setter");
        this.$store.dispatch("setEditor", value);
      },
    },
  },
};
</script>

<style lang="scss" scoped>
@use "../assets/scss/components/PageEditor";
</style>

editor.js(store module):

  state: {
    editorData: [],
    editorLoading: false,
  },
  mutations: {
    SAVE_EDITOR(state, data) {
      state.editorData = data;
    },
    TOGGLE_EDITOR_LOAD(state, busy) {
      state.editorLoading = busy;
    },
  },
  actions: {
    setEditor({ commit }, data) {
      commit("SAVE_EDITOR", data);
    },
    loadEditor({ commit }) {
      commit("TOGGLE_EDITOR_LOAD", true);

      //TODO: Change me to read API DATA
      let fakeData = [
        {
          tagName: "section",
          attributes: {
            id: "section-1",
            class: "test",
          },
          children: [
            {
              tagName: "div",
              attributes: {
                id: "container-1",
                class: "container",
              },
              children: [
                {
                  tagName: "div",
                  attributes: {
                    id: "row-1",
                    class: "row",
                  },
                  children: [
                    {
                      tagName: "div",
                      attributes: {
                        id: "col-1",
                        class: "col",
                      },
                      children: [],
                    },
                    {
                      tagName: "div",
                      attributes: {
                        id: "col-2",
                        class: "col",
                      },
                      children: [
                        {
                          tagName: "p",
                          attributes: {
                            id: "p-1",
                            class: "p",
                          },
                          textNode: "This is my paragraph",
                          children: [],
                        },
                      ],
                    },
                  ],
                },
              ],
            },
          ],
        },
      ];
      commit("SAVE_EDITOR", fakeData);
      commit("TOGGLE_EDITOR_LOAD", false);
    },
  },
  getters: {
    getEditorData: (state) => state.editorData,
    getEditorLoading: (state) => state.editorLoading,
  },
};

It seems only dragging labels works for moving stuff around but not like i'd expect. I think this makes sense but why can't I drag the body anywhere? It's not slotted in header or footer and the docs says that's the onlytime it wouldn't be? Can I not use as the tag itself and drag it?

One more note, as far as draggable goes, I can't render the tags inside of draggable as draggable wraps the tags in a div or a span which sucks because if you wrap a col in a div that's inside a row it breaks bootstrap :(

As I'm sure all of you can deduce the behavior I'm expecting, anything should be draggable into any section(in the future I want this to change so only cols can be dragged into rows, rows can only be dragged into sections etc(but for now I'm not sure how to do this so we start at the beginning :D)).

Also yes I know those components are kinda messy atm, until I fix this I'm not cleaning them up as I keep drastically changing the contents of these files trying to make it work, sorry its hacky atm!

Any help or ideas would be amazing!

Thanks guys!

mubasshir commented 3 years ago

Hi, were you able to solve it?

Celtech commented 3 years ago

I was yes! I used it to create a pretty darn cool landing page builder!

On Thu, Aug 5, 2021 at 7:18 AM Mubasshir Pawle @.***> wrote:

Hi, were you able to solve it?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/SortableJS/Vue.Draggable/issues/1055#issuecomment-893413363, or unsubscribe https://github.com/notifications/unsubscribe-auth/AB4UWVE2IOLN4EPJIIDXR7LT3J6SNANCNFSM47VAI7YA .