JSFiddle - React, Tailwind, and code Playground

by Sam Wray

HTML

<script src="https://unpkg.com/vue"></script>

<div id="app">
  <div id="grid">
    <div v-for="x, xi in grid" class="col">
      <transition-group name="list" tag="span">
        <div
          v-for="y, yi in x"
          class="block"
          :style="`background-color: ${y.color}`"
          @click="blockClicked(xi, yi, y.color)"
          :key=" y.id"
        ></div>
      </transition-group>
    </div>
  </div>
</div>

SCSS

@font-stack: Helvetica,
sans-serif;
@primary-color: #999;
html,
body {
  width: 100%;
  height: 100%;
}

body {
  font: 10pt @font-stack;
  color: @primary-color;
  margin: 0;
  padding: 0;
}

#grid,
#app {
  width: 100%;
  height: 100%;
  background: grey;
  font-size: 0;
}

.col {
  background: grey;
  float: left;
  width: 10%;
  height: 100%;
}

.block {
  width: 100%;
  height: 10%;
  background: red;
  transition: all;
  display: inline-block
}

.active {
  -webkit-box-shadow: inset 0 0 10px #000000;
}

.list-move {
  transition: transform 400ms cubic-bezier(0.68, 0.05, 1, 0.93);
}

JavaScript

const COLORS = ['red', 'green', 'blue', 'yellow'];
const MAX_X = 10;
const MAX_Y = 10;
const deadColor = 'transparent';

class Block {
  constructor(x, y, id) {
    this.id = Date.now() + id;
    this.x = x;
    this.y = y;
    this.color = COLORS[
      Math.floor(Math.random() * COLORS.length)
    ];
  }
}

new Vue({
  el: '#app',
  data: {
    grid: [],
  },
  created() {
    for (let x = 0; x < MAX_X; x++) {
      let col = [];
      for (let y = 0; y < MAX_Y; y++) {
        col.push(new Block(x, y, '' + x + y));
      }

      this.grid.push(col);
    }
  },
  methods: {
    blockClicked(x, y, color) {
        const {
          grid
        } = this;
        const block = grid[x][y];

        if (block.color === deadColor) return;

        this.checkBlocks(color, x, y);
        block.color = deadColor;

        for (let x = 0; x < MAX_X; x++) {
          const toSplice = [];

          for (let y = 0; y < MAX_Y; y++) {
            const block = grid[x][y];
            if (block.color === deadColor) {
              toSplice.push(y);
            }
          }

          if (toSplice.length) {
            const deadBlocks = [];

            for (let i = toSplice.length - 1; i >= 0; i--) {
              const y = toSplice[i];

              deadBlocks.push(
                grid[x].splice(y, 1)[0]
              );
            }

            grid[x] = deadBlocks.concat(grid[x]);
          }
        }
      },
      getAdjacents(x, y) {
        // return [top, right, bottom, left]
        return [
          [x - 1, y],
          [x, y + 1],
          [x + 1, y],
          [x, y - 1]
        ];
      },
      matchAdjacentColors(adjacents, color, x, y) {
        // return [top, right, bottom, left]
        return adjacents.map((point) => {
          const x = point[0];
          const y = point[1];
          if (x > MAX_X - 1 || y > MAX_Y - 1 || x < 0 || y < 0) {
            return -1;
          }

          const block = this.grid[x][y];
          return...