block by nitaku 7fd3c55874fc889af14d69e0e13e95cb

Vue.js bar chart component

Full Screen

An exercise that displays three flexible bar chart components linked to the same data, using vue.js.

index.js

// Generated by CoffeeScript 1.10.0
(function() {
  var app;

  Vue.component('bar-chart', {
    props: {
      data: {
        type: Array,
        required: true
      },
      padding: {
        type: Number,
        "default": 1
      },
      color: {
        type: String,
        "default": 'gray'
      }
    },
    data: function() {
      return {
        width: 200,
        height: 200
      };
    },
    template: '<svg class="bar-chart">\n  <rect\n    class="bar"\n    v-for="d in layout"\n    :x="d.x"\n    :y="d.y"\n    :width="d.width"\n    :height="d.height"\n    :fill="color"\n  />\n</svg>',
    created: function() {
      this.x = d3.scaleLinear();
      return this.y = d3.scaleLinear();
    },
    mounted: function() {
      this.width = this.$el.clientWidth;
      return this.height = this.$el.clientHeight;
    },
    computed: {
      layout: function() {
        this.x.domain([0, this.data.length]).range([0, this.width]);
        this.y.domain([
          0, d3.max(this.data, function(d) {
            return d.v;
          })
        ]).range([0, this.height]);
        return this.data.map((function(_this) {
          return function(d, i) {
            return {
              v: d.v,
              x: _this.x(i),
              y: _this.y.range()[1] - _this.y(d.v),
              width: Math.max(1, _this.x(1) - _this.x(0) - _this.padding),
              height: _this.y(d.v)
            };
          };
        })(this));
      }
    }
  });

  app = new Vue({
    el: '#app',
    data: {
      values: [
        {
          v: 10
        }, {
          v: 20
        }, {
          v: 30
        }
      ]
    },
    methods: {
      add_random_datapoint: function() {
        return this.values.push({
          v: Math.random() * 300
        });
      }
    }
  });

}).call(this);

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Vue.js Bar Chart Component</title>
  <link type="text/css" href="index.css" rel="stylesheet"/>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
  
  <div id="app" class="vertical">
    <button id="button" v-on:click="add_random_datapoint">
      Add random datapoint
    </button>
    
    <div class="horizontal">
      <bar-chart
        v-bind:data="values"
        :padding="1"
      />

      <div class="vertical">
        <bar-chart
          v-bind:data="values"
          :padding="1"
          color="orange"
        />
        <bar-chart
          v-bind:data="values"
          :padding="3"
          color="teal"
        />
      </div>
      
    </div>
    
  </div>
  
  <script src="index.js"></script>
</body>
</html>

index.coffee

Vue.component 'bar-chart',
  props:
    data:
      type: Array
      required: true
    padding:
      type: Number
      default: 1
    color:
      type: String
      default: 'gray'
  data: () ->
    width: 200
    height: 200
  template: '''
    <svg class="bar-chart">
      <rect
        class="bar"
        v-for="d in layout"
        :x="d.x"
        :y="d.y"
        :width="d.width"
        :height="d.height"
        :fill="color"
      />
    </svg>
  '''
  created: () ->
    @x = d3.scaleLinear()
    @y = d3.scaleLinear()
    
  mounted: () ->
    @width = @$el.clientWidth
    @height = @$el.clientHeight
  
  computed:
    layout: () ->
      @x 
        .domain [0, @data.length]
        .range [0, @width]
      @y
        .domain [0, d3.max @data, (d) -> d.v]
        .range [0, @height]
        
      return @data.map (d, i) => {
        v: d.v,
        x: @x(i),
        y: @y.range()[1]-@y(d.v),
        width: Math.max(1,@x(1)-@x(0)-@padding),
        height: @y(d.v)
      }

app = new Vue
  el: '#app'
  data:
    values: [{v:10},{v:20},{v:30}]
  methods:
    add_random_datapoint: () ->
      @values.push {v: Math.random()*300}
      

index.css

body, html {
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
}
#app {
  width: 100%;
  height: 100%;
}
.horizontal {
  display: flex;
  flex-direction: row;
  flex-grow: 1;
}
.vertical {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
}
.bar-chart {
  flex-grow: 1;
  margin: 6px;
}
button {
  height: 32px;
  font-weight: bold;
}