JSFiddle - React, Tailwind, and code Playground

by Daedalus

HTML

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<div class="row">
  <div class="col-2">
    <div class="row" :style="squareParent.style" v-for="(squareParent, i) in squareParents" ref="titles">
      <div class="col m-0" style="height: 20px; border: 1px solid transparent;">
        {{squareParent.title}}
      </div>
      <div class="col" :style="{backgroundColor: squareParent.color, width: '20px', height: '20px', margin: '2px'}"></div>
    </div>
  </div>
  <div class="col-4 position-relative">
    <div v-for="(squareParent, i) in squareParents">
      <div :style="{backgroundColor: squareParent.color, width: '20px', height: '20px', margin: '2px', float: 'left', position: 'relative'}" v-for="(square, square_i) in squareParent.squares" ref="squares"></div>
    </div>
  </div>
</div>

JavaScript

new Vue({
  el: '#app',
  data: {
    squareParents: [{
      title: 'test1',
      squares: 1,
      color: 'red',
      style: {}
    }, {
      title: 'test2',
      squares: 1,
      color: 'green',
      style: {}
    }, {
      title: 'test3',
      squares: 1,
      color: 'blue',
      style: {}
    }, {
      title: 'test4',
      squares: 24,
      color: 'red',
      style: {}
    }, {
      title: 'test5',
      squares: 21,
      color: 'green',
      style: {}
    }, {
      title: 'test6',
      squares: 23,
      color: 'blue',
      style: {}
    }]
  },
  methods: {
    async styleTitles() {
      let squareIdx = 0;
      for (let [idx, squareParent] of this.squareParents.entries()) {
        let spacing;
        if (idx > 0) {
          let square = this.$refs.squares[squareIdx],
            title = this.$refs.titles[idx],
            priorTitle = this.$refs.titles[idx - 1],
            sTop = square.getBoundingClientRect().top,
            tTop = title.getBoundingClientRect().top,
            pTTop = priorTitle.getBoundingClientRect().top;
          if (tTop < sTop) {
            spacing = (sTop - pTTop) - square.getBoundingClientRect().height - 6;
          }
        }
        if (!isNaN(spacing)) {
          squareParent.style = {
            marginTop: spacing + 'px'
          };
          await this.$nextTick();
        }
        squareIdx += squareParent.squares;
      }
    }
  },
  mounted() {
    this.styleTitles();
  }
});