Vue Simple Component

by hxtpoe

HTML

<div id="app">
  <template id="graph">
    <div class="n9-graph-box">
      <svg viewBox="0 0 150 150">
      
        <rect name="totalBudget" x="{{leftGraphPadding}}" y="{{topGraphPadding}}" width="{{graphWidth}}" height="{{normalizedTotalBudget}}" transform="rotate(180, {{leftGraphPadding + (graphWidth/2) }}, {{topGraphPadding + 100/2}})" style="fill:#e6e6e6; stroke:#e6e6e6;stroke-width:{{strokeWidth}}" />
      
       <rect name="prediction" x="{{leftGraphPadding}}" y="{{topGraphPadding}}" width="{{graphWidth}}" height="{{normalizedPrediction}}" transform="rotate(180, {{leftGraphPadding + (graphWidth/2) }}, {{topGraphPadding + 100/2}})" style="fill:white;stroke:{{predictionColor}};stroke-width:{{strokeWidth}};fill-opacity:0.0;" />
      
        <rect name="consumed" x="{{leftGraphPadding}}" y="{{ topGraphPadding }}" width="{{graphWidth}}" height="{{normalizedConsumedBudget}}" transform="rotate(180, {{leftGraphPadding + (graphWidth/2) }}, {{topGraphPadding + 100/2}})" style="fill:{{consumedBudgetColor}}; stroke:{{consumedBudgetColor}};stroke-width:{{strokeWidth}} " />
      
              <line v-if="consumedBudget > totalBudget" x1="{{leftGraphPadding}}" y1="{{topGraphPadding + normalizedTotalBudget}}" x2="{{graphWidth + leftGraphPadding}}" transform="rotate(180, {{leftGraphPadding + (graphWidth/2)}}, {{(100 / 2) + topGraphPadding}})" y2="{{topGraphPadding + normalizedTotalBudget}}" stroke="white" stroke-dasharray="3" />
              
              
      <polygon points="0,0 0,{{triangularSize * 2}} {{triangularSize * 2}},{{triangularSize}}" transform='translate({{leftMarkerPadding - (triangularSize * 3) }} {{topGraphPadding + (100-normalizedTotalBudget - (1.5*triangularSize))}})'/>
      <polygon points="0,0 0,{{triangularSize * 2}} {{triangularSize * 2}},{{triangularSize}}" transform='translate({{leftMarkerPadding - (triangularSize * 3) }} {{topGraphPadding + (100-normalizedConsumedBudget - (1.5*triangularSize))}})'/>
     <polygon points="0,0...

CSS

body {
  background: white;
}

.n9-graph-box {
  width: 300px;
  height: 300px;
}



.presentation {
  display: flex;
}

/* text.label  {
  fill: red;
} */

Babel + JSX

/* To DO add formating to prevent 33.3333333333333 values */
Vue.component('graph', {
  template: '#graph',
  props: ["values"],
  data() {
    return {
      strokeWidth: 2,
      leftGraphPadding: 100,
      leftLabelPadding: 100,
      leftMarkerPadding: 100,
      topGraphPadding: 20,
      graphWidth: 30,
      negativePredictionColor: '#ff0000',
      positivePredictionColor: '#32CD32',
      triangularSize: 2,
    }
  },
  created() {
    this.consumedBudget = this.values.consumedBudget;
    this.totalBudget = this.values.totalBudget;
    this.prediction = this.values.prediction;
    // here the size of rects are calculated - 
    // To make life easier, graph has always 100 of height. So the highest percentage is always height 100, other values are scaled down, using "sopghisticated division' operation
    let values = [this.consumedBudget, this.totalBudget, this.prediction];
    
    let compare = function(a,b) {
    	if (a<b) {
	      return 1
      } else {
      	return -1
      }
    }
    
    this.scale =  100 / values.sort(compare)[0];
  },
  methods: {
  	
  },
  computed: {
		  normalizedTotalBudget: function(x) {
	 			return parseInt(this.totalBudget * this.scale);
			},
      normalizedConsumedBudget: function(x) {
				 return parseInt(this.consumedBudget * this.scale);
		  },
      normalizedPrediction: function(x) {
		          return parseInt(this.prediction * this.scale);
      },
	  	predictionColor: function() { 
        if (this.prediction > this.totalBudget) {
          return this.negativePredictionColor;
        } else {
            return this.positivePredictionColor;
        }
      },
      consumedBudgetColor: function() { 
        if (this.consumedBudget > this.totalBudget) {
          return this.negativePredictionColor;
        } else {
            return "gray";
        }
		  }
      },
});

new Vue({
  el: '#app'
});