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'
});