<script setup>
import DragGrid from "../../src/DragGrid.vue";
import CircularCard from "./components/CircularCard.vue";
import NumberCounter from "./components/NumberCounter.vue";
</script>

<template>
  <div id="app">
    <h1><code>DragGrid</code> Examples</h1>
    <h2>Example 1</h2>
    <p>
      Grid with two programmatically blocked cells and one programmatically
      blocked item
    </p>
    <DragGrid
      :rows="8"
      :cols="5"
      :pos-validation="blockField"
      v-model="items"
      class="bordered"
    >
      <div id="blocker">
        This field is blocked because it's filled, the next one programmatically
      </div>
      <template #item="item">
        <div class="container">{{ item }}</div>
      </template>
    </DragGrid>

    <h2>Example 2: "Tic-Tac-Toe"</h2>
    <p>
      A grid functioning as a playing field and another functioning as a
      container for playing pieces. You can drag as many pieces as you want from
      the container to the field.
    </p>
    <div class="ttt-container">
      <DragGrid
        :rows="3"
        :cols="3"
        v-model="ticTacToe1"
        class="tic-tac-toe"
        context="ticTacToe"
        :validate-element="randomKey"
      >
        <template #item="item">
          <CircularCard>
            {{ item.key.startsWith("a") ? "X" : "O" }}
          </CircularCard>
        </template>
      </DragGrid>
      <div>
        <p>
          These two are two different grids but we can drag from right to left!
        </p>
        <p>Drag items from the container on the right to play on the left.</p>
      </div>
      <DragGrid
        :rows="1"
        :cols="2"
        v-model="ticTacToe2"
        class="tic-tac-toe"
        context="ticTacToe"
        :pos-validation="blockAllMoving"
      >
        <template #item="item">
          <CircularCard>
            {{ item.data.text }}
          </CircularCard>
        </template>
      </DragGrid>
    </div>

    <h2>Example 3: Counters</h2>
    <p>
      Showcasing local and global state: The local state of the counter
      components doesn't change when moved inside one grid, but is cleared when
      moved to the other. The global state is in the <code>data</code> attribute
      of each item and gets transferred as well. Both grids have also custom
      highlights.
    </p>
    <div class="ttt-container">
      <drag-grid
        v-model="counters"
        :cols="3"
        :rows="2"
        id="c-grid"
        @input="logInput"
        context="counter"
      >
        <template #item="item">
          <number-counter v-model="item.data.num"></number-counter>
        </template>
        <template #highlight>
          <div ref="highlight" class="custom-highlight">
            Das hier ist das Highlight
          </div>
        </template>
      </drag-grid>
      <span>← Drag here please →</span>
      <drag-grid v-model="counters2" :cols="3" :rows="2" context="counter">
        <template #item="item">
          <number-counter v-model="item.data.num"></number-counter>
        </template>
        <template #highlight>
          <div ref="highlight" class="custom-highlight">
            Das hier ist das Highlight
          </div>
        </template>
      </drag-grid>
    </div>

    <h2>Example 4: Dynamic lessons</h2>
    <p>
      These lessons are loaded from <code>computed</code> to simulate a
      non-editable source like an API. they are changed using the method
      <code>handleLessonMoved</code>. Try changing the sizes of Lesson1, the
      grid and the texts of the items! The lesson container on the side doesn't
      have a highlight and doesn't keep track of the item position. Try moving
      <code>item3</code> back and forth!
    </p>
    <div class="ttt-container">
      <div>
        <label>
          Lesson 1 has length: {{ lesson1Length }}
          <input
            type="range"
            v-model="lesson1Length"
            min="1"
            max="5"
            step="1"
          />
        </label>
        <input type="text" v-model="lessonData.lesson1" />
      </div>
      <div>
        <label>
          Lesson grid has width: {{ lesson1Cols }}
          <input type="range" v-model="lesson1Cols" min="2" max="5" step="1" />
        </label>
        <input type="text" v-model="lessonData.lesson2" />
      </div>
      <div>
        <label>
          Lesson grid has height: {{ lesson1Rows }}
          <input type="range" v-model="lesson1Rows" min="6" max="10" step="1" />
          <input type="text" v-model="lessonData.lesson3.inside" />
          <input type="text" v-model="lessonData.lesson3.outside" />
        </label>
      </div>

      <drag-grid
        :value="lessons1"
        :cols="parseInt(lesson1Cols)"
        :rows="parseInt(lesson1Rows)"
        class="bordered"
        context="lesson"
        grid-id="lesson-plan"
        @itemChanged="handleLessonMoved"
      >
        <template #item="item">
          <div class="container">{{ item }}</div>
        </template>
      </drag-grid>
      <drag-grid
        :value="lessons1"
        :cols="1"
        :rows="10"
        context="lesson"
        grid-id="lesson-storage"
        @itemChanged="handleLessonDropInContainer"
        no-highlight
      >
        <template #item="item">
          <div class="container">{{ item }}</div>
        </template>
      </drag-grid>
    </div>

    <h2>Example 5: Dynamic colors</h2>
    <p>
      This example showcases the <code>rawItem</code> with the custom method
      <code>getColor</code>. Both grids on the outside call the method with
      <code>"red"</code> while the one in the middle uses <code>"green"</code>.
      We use arrays called <code>placedA</code>, <code>placedB</code> and
      <code>placedC</code> to save, which item is contained in which grid.
      Notice: the third grid doesn't save the item position, they are always
      positioned automatically.
    </p>
    <div class="ttt-container">
      <drag-grid
        :value="colorGrid"
        :cols="4"
        :rows="4"
        grid-id="gridA"
        context="colors"
        class="bordered color-grid"
        @itemChanged="handleColorItemChange"
      >
        <template #item="item">
          <div
            :style="{
              background: item.rawItem.getColor('red'),
              color: 'white',
            }"
          >
            {{ item.data.name }}
          </div>
        </template>
      </drag-grid>
      <drag-grid
        :value="colorGrid"
        :cols="4"
        :rows="4"
        grid-id="gridB"
        context="colors"
        class="bordered color-grid"
        @itemChanged="handleColorItemChange"
      >
        <template #item="item">
          <div
            :style="{
              background: item.rawItem.getColor('green'),
              color: 'black',
            }"
          >
            {{ item.data.name }}
          </div>
        </template>
      </drag-grid>
      <drag-grid
        :value="colorGrid"
        :cols="4"
        :rows="4"
        grid-id="gridC"
        context="colors"
        class="bordered color-grid"
        @itemChanged="handleColorItemChange"
      >
        <template #item="item">
          <div
            :style="{
              background: item.rawItem.getColor('red'),
              color: 'white',
            }"
          >
            {{ item.data.name }}
          </div>
        </template>
      </drag-grid>
    </div>

    <div class="ttt-container">
      <div>
        <h2>Example 6: Disabled grid</h2>
        <p>
          This uses the same data as the tic tac toe but is completely disabled.
          Notice how the items still move if the tic tac toe grid above is
          changed. Uncheck the checkbox to enable:
        </p>

        <label>
          <input type="checkbox" v-model="gridDisabled" />
          Grid disabled?
        </label>

        <drag-grid
          v-model="ticTacToe1"
          :cols="3"
          :rows="3"
          :disabled="gridDisabled"
          class="tic-tac-toe"
          context="ticTacToe"
          :validate-element="randomKey"
        >
          <template #item="item">
            <CircularCard>
              {{ item.key.startsWith("a") ? "X" : "O" }}
            </CircularCard>
          </template>
        </drag-grid>
      </div>
      <div>
        <h2>Example 7: Disabled fields and items with props</h2>
        <p>
          This is a grid with disabled fields and items. Red items are disabled
          and cannot be moved.
        </p>
        <drag-grid
          v-model="someDisabledItems"
          :cols="4"
          :rows="4"
          class="tic-tac-toe"
          :disabled-fields="disabledFields"
        >
          <template #item="{ rawItem }">
            <div
              class="container"
              :style="{ background: rawItem.disabled ? 'red' : 'green' }"
            ></div>
          </template>
          <template #disabledField
            ><div class="container">This field is disabled!</div></template
          >
        </drag-grid>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    blockField(x, y, key) {
      // We won't move fields with ID 'obj8' and nothing into (3, 3) and (4, 3)
      if (x === 3 && y === 3) return false;
      if (x === 4 && y === 3) return false;
      return key !== "obj8";
    },
    blockAllMoving() {
      return false;
    },
    randomKey(element) {
      if (element.key.length !== 1) return;
      element.key += Math.random().toString(36).replace("0.", "");
    },
    logInput(input) {
      console.log("New movement detected:", input);
    },
    handleLessonMoved(lesson) {
      this.logInput(lesson);
      switch (lesson.key) {
        case "lesson1":
          this.lesson1X = lesson.x;
          this.lesson1Y = lesson.y;
          break;
        case "lesson2":
          this.lesson2X = lesson.x;
          this.lesson2Y = lesson.y;
          break;
        case "lesson3":
          this.lesson3X = lesson.x;
          this.lesson3Y = lesson.y;
          this.lesson3inPlan = true;
          break;
        default:
          console.error("Something is wrong here!");
      }
    },
    handleLessonDropInContainer(lesson) {
      if (lesson.key === "lesson3") this.lesson3inPlan = false;
    },
    contains(array, search) {
      return array.filter((obj) => obj === search).length > 0;
    },
    findAndRemove(array, element) {
      let index = array.findIndex((i) => {
        return i === element;
      });
      if (index >= 0) array.splice(index, 1);
    },
    handleColorItemChange(item) {
      this[item.key].x = item.x;
      this[item.key].y = item.y;

      if (item.gridId === item.originGridId) return;

      switch (item.gridId) {
        case "gridA":
          // Moved to gridA
          this.findAndRemove(
            item.originGridId === "gridB" ? this.placedB : this.placedC,
            item.key
          );
          this.placedA.push(item.key);
          break;
        case "gridB":
          // Moved to gridB
          this.findAndRemove(
            item.originGridId === "gridA" ? this.placedA : this.placedC,
            item.key
          );
          this.placedB.push(item.key);
          break;
        case "gridC":
          // Moved to gridC
          this.findAndRemove(
            item.originGridId === "gridB" ? this.placedB : this.placedA,
            item.key
          );
          this.placedC.push(item.key);
          break;
      }
    },
  },
  data() {
    return {
      items: [
        { x: 1, y: 3, w: 1, h: 1, key: "obj1", data: {} },
        { x: 2, y: 1, w: 1, h: 1, key: "obj2", data: {} },
        { x: 3, y: 1, w: 2, h: 2, key: "obj3", data: {} },
        { x: 5, y: 2, w: 1, h: 1, key: "obj4", data: {} },
        { x: 1, y: 1, w: 1, h: 2, key: "obj5", data: {} },
        { x: 5, y: 1, w: 1, h: 1, key: "obj6", data: {} },
        { x: 2, y: 2, w: 1, h: 3, key: "obj7", data: {} },
        {
          x: 1,
          y: 4,
          w: 1,
          h: 1,
          key: "obj8",
          data: { title: "I'm blocked from moving!" },
        },
        { x: 5, y: 3, w: 1, h: 1, key: "obj9", data: {} },
      ],
      ticTacToe1: [
        { x: 1, y: 1, w: 1, h: 1, key: "a1", data: { text: "X" } },
        { x: 3, y: 3, w: 1, h: 1, key: "b1", data: { text: "O" } },
      ],
      ticTacToe2: [
        { x: 1, y: 1, w: 1, h: 1, key: "a", data: { text: "X" } },
        { x: 2, y: 1, w: 1, h: 1, key: "b", data: { text: "O" } },
      ],
      counters: [
        { x: 1, y: 1, w: 1, h: 1, key: "a", data: { num: 1 } },
        { x: 2, y: 1, w: 1, h: 1, key: "b", data: { num: 4 } },
        { x: 3, y: 2, w: 1, h: 1, key: "c", data: { num: -1 } },
      ],
      counters2: [
        { x: 1, y: 2, w: 1, h: 1, key: "d", data: { num: 30 } },
        { x: 3, y: 1, w: 1, h: 1, key: "e", data: { num: 3 } },
      ],
      lesson1Length: 1,
      lesson1Cols: 4,
      lesson1Rows: 6,
      lesson1X: 1,
      lesson1Y: 1,
      lesson2X: 2,
      lesson2Y: 1,
      lesson3X: -1,
      lesson3Y: -1,
      lesson3inPlan: false,
      lessonData: {
        lesson1: "Nothing",
        lesson2: "Hallo",
        lesson3: {
          inside: "This is lesson 3 inside the plan",
          outside: "This is lesson3 outside of the plan",
        },
      },
      placedA: ["item1", "item2"],
      placedB: ["item3", "item4"],
      placedC: ["item5", "item6"],
      item1: {
        x: 1,
        y: 1,
      },
      item2: {
        x: 2,
        y: 2,
      },
      item3: {
        x: 1,
        y: 1,
      },
      item4: {
        x: 3,
        y: 3,
      },
      item5: {
        x: 2,
        y: 4,
      },
      item6: {
        x: 4,
        y: 2,
      },
      gridDisabled: true,
      someDisabledItems: [
        { key: "key1", x: 1, y: 3, w: 1, h: 1, data: {} },
        { key: "key2", x: 2, y: 2, w: 1, h: 1, data: {} },
        { key: "key3", x: 3, y: 1, w: 1, h: 1, data: {}, disabled: true },
        { key: "key4", x: 1, y: 2, w: 1, h: 1, data: {}, disabled: true },
      ],
      disabledFields: [
        { x: 1, y: 1 },
        { x: 2, y: 3 },
      ],
    };
  },
  computed: {
    lessons1() {
      return [
        {
          x: (grid) => {
            return grid.gridId === "lesson-plan" ? this.lesson1X : 0;
          },
          y: (grid) => {
            return grid.gridId === "lesson-plan" ? this.lesson1Y : 0;
          },
          w: 1,
          h: this.lesson1Length,
          key: "lesson1",
          data: {
            text: this.lessonData.lesson1,
          },
        },
        {
          x: (grid) => {
            return grid.gridId === "lesson-plan" ? this.lesson2X : 0;
          },
          y: (grid) => {
            return grid.gridId === "lesson-plan" ? this.lesson2Y : 0;
          },
          w: 1,
          h: parseInt(this.lesson1Length) + 1,
          key: "lesson2",
          data: {
            text: this.lessonData.lesson2,
          },
        },
        {
          x: (grid) => {
            if (this.lesson3inPlan) {
              return grid.gridId === "lesson-plan" ? this.lesson3X : -1;
            }
            return grid.gridId === "lesson-plan" ? -1 : 0;
          },
          y: (grid) => {
            if (this.lesson3inPlan) {
              return grid.gridId === "lesson-plan" ? this.lesson3Y : -1;
            }
            return grid.gridId === "lesson-plan" ? -1 : 0;
          },
          w: 1,
          h: parseInt(this.lesson1Length) + 1,
          key: "lesson3",
          data: (grid) => {
            return {
              text:
                grid.gridId === "lesson-storage"
                  ? this.lessonData.lesson3.outside
                  : this.lessonData.lesson3.inside,
            };
          },
        },
      ];
    },
    colorGrid() {
      let items = ["item1", "item2", "item3", "item4", "item5", "item6"];
      let greens = [
        "green",
        "seagreen",
        "greenyellow",
        "darkolivegreen",
        "limegreen",
        "palegreen",
      ];
      let reds = [
        "darkred",
        "indianred",
        "orangered",
        "red",
        "maroon",
        "firebrick",
      ];
      return items.map((itemName) => {
        return {
          key: itemName,
          x: (grid) => {
            if (grid.gridId === "gridA") {
              return this.contains(this.placedA, itemName)
                ? this[itemName].x
                : -1;
            } else if (grid.gridId === "gridB") {
              return this.contains(this.placedB, itemName)
                ? this[itemName].x
                : -1;
            } else {
              // gridC
              return this.contains(this.placedC, itemName) ? 0 : -1;
            }
          },
          y: (grid) => {
            if (grid.gridId === "gridA") {
              return this.contains(this.placedA, itemName)
                ? this[itemName].y
                : -1;
            } else if (grid.gridId === "gridB") {
              return this.contains(this.placedB, itemName)
                ? this[itemName].y
                : -1;
            } else {
              // gridC
              return this.contains(this.placedC, itemName) ? 0 : -1;
            }
          },
          w: 1,
          h: 1,
          data: {
            name: itemName,
          },
          getColor: (a) =>
            a === "red"
              ? reds[parseInt(itemName[4]) - 1]
              : greens[parseInt(itemName[4]) - 1],
        };
      });
    },
  },
};
</script>

<style scoped>
#blocker {
  grid-row: 3 / span 1;
  grid-column: 3 / span 1;
  background-image: linear-gradient(
    45deg,
    #edd85f 25%,
    #0f2b3d 25%,
    #0f2b3d 50%,
    #edd85f 50%,
    #edd85f 75%,
    #0f2b3d 75%,
    #0f2b3d 100%
  );
  background-size: 56.57px 56.57px;
  color: white;
  font-size: large;
  font-weight: bold;
  text-shadow: 4px 4px 4px #2c3e50;
}

.container {
  background: lightcoral;
  width: 100%;
  height: 100%;
}

.tic-tac-toe {
  max-width: 400px;
}

.ttt-container {
  display: flex;
  justify-content: space-between;
}

.custom-highlight {
  display: flex;
  align-items: center;
  justify-content: center;
  background: aquamarine;
  width: 100%;
  height: 100%;
}

.bordered {
  border: 2px solid grey;
}

.color-grid {
  min-height: 11em;
  line-height: 2em;
  box-sizing: content-box;
}

h2 {
  padding-top: 1em;
}

h2 + p {
  padding-bottom: 0.5em;
}

code {
  background: lightgray;
  padding: 0.2em;
  border-radius: 3px;
}
</style>