Vue3 with Gridstack: widgets don't move 1 column at a time when dragged

355 Views Asked by At

I am working with gridstack (v. 7.2.3) and vue3 (v.3.2.45) and so far everything works except that when I drag a widget instead of moving one column space at a time it snaps to the next available space of the size of the widget. This means that I cannot place my widgets wherever I want on the grid and also the behaviour is unpredictable.

Snapping to next available space. Cannot place next to another widget.

This is the movement I would like to achieve. This comes from the official gridstack vue3 with v-for demo

I have tried two approaches with gridstack and vue3.

  1. I have a gridstack component and a gridstackItem component and I bring them together in a third container component (i.e. in the App component)
  2. I have 1 gridstack component where I nest simple <div></div> to make the gridstack items. And then put the gridstack component in a container component.

Here is a link to a code sandbox of the first attempt where you can see the problem. The second attempt worked in a sandbox, but did not work when I tried it in my code and cannot figure out why, but I suspect it may have to do with how css works with vue. In my code for this second attempt the gridstack component is nested quite deeply in many vue components and is in a component that has position:absolute;

I have also tried using float for the grid but that didn't work. In fact float caused my code to crash or freeze when set to true.

Here is the code to reproduce:

Gridstack component

<template>
  <div id="grid-container">
    <div class="grid-stack">
      <slot></slot>
    </div>
  </div>
</template>
<script>
import { GridStack } from "gridstack";
import "gridstack/dist/gridstack.min.css";
import "gridstack/dist/gridstack-extra.min.css";

export default {
  name: "GridStack",
  data() {
    return {
      instance: null,
    };
  },
  mounted() {
    this.instance = GridStack.init({ minRow: 1, float: false });
  },
  methods: {
    addWidget(item) {
      console.debug("Add WIDGET");
      this.$nextTick(() => {
        console.log("ITEM", item);
        const selector = `#${item.grid.id}`;
        this.instance.makeWidget(selector);
      });
    },
    removeWidget() {
      console.log("Removing widget");
    },
  },
  provide() {
    return {
      addWidget: this.addWidget,
    };
  },
};
</script>
<style scoped>
#grid-container {
  border: solid;
  border-width: 0.1rem;
  background: lightyellow;
}
</style>

GridstackItem

<template>
  <div :id="id" :gs-id="id" :gs-h="h" :gs-w="w" :gs-x="x" :gs-y="y">
    <div class="grid-stack-item-content">
      <slot name="Title"></slot>
      <slot name="Remove"></slot>
    </div>
  </div>
</template>
<script>
export default {
  name: "GridtStackItem",
  props: {
    id: String,
    x: Number,
    y: Number,
    w: Number,
    h: Number,
    item: Object,
  },
  mounted() {
    this.addWidget(this.item);
  },
  inject: ["addWidget"],
};
</script>
<style scoped>
.grid-stack-item-content {
  background: #ffaa00aa;
}
</style>

App Component

<template>
  <div id="main-container">
    <Header title="Grid stack" :callbackAdd="addItem" />
    <GridStack ref="gs">
      <GridStackItem
        v-for="item in items"
        :key="item.grid.id"
        :id="item.grid.id"
        :w="item.grid.w"
        :h="item.grid.h"
        :x="item.grid.x"
        :y="item.grid.y"
        :item="item"
      >
        <template #Title
          ><h5>{{ item.title }}</h5></template
        >
      </GridStackItem>
    </GridStack>
  </div>
</template>

<script>
import Header from "@/components/Header";
import GridStack from "@/components/GridStack";
import GridStackItem from "@/components/GridStackItem";

export default {
  name: "App",
  components: {
    Header,
    GridStack,
    GridStackItem,
  },
  data() {
    return {
      items: [
        {
          title: "Widget Title-0",
          grid: { w: 2, h: 2, x: 0, y: 0, id: "grid-1" },
        },
        {
          title: "Widget Title-1",
          grid: { w: 2, h: 2, x: 0, y: 0, id: "grid-0" },
        },
      ],
      counter: 2,
    };
  },
  methods: {
    addItem() {
      console.debug("Add ITEM");
      const newItem = {
        title: "Widget Title-" + this.counter,
        grid: { w: 2, h: 2, x: 0, y: 0, id: "grid-" + this.counter },
      };
      this.items.push(newItem);
      this.counter++;
    },
  },
  mounted() {},
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
hr {
  margin: 1rem;
}
.grid-stack-item-content {
  background: orange;
  padding: 1rem;
  overflow: scroll;
}
</style>

Here is a link to the second attempt that works.

0

There are 0 best solutions below