DataVis.vue
1 <template lang='pug'> 2 .datavisbox 3 #my_dataviz 4 button(@click='drawVis') draw connection map 5 </template> 6 7 <script> 8 import * as d3 from 'd3' 9 import forceBoundary from 'd3-force-boundary' 10 import calculations from '../calculations.js' 11 12 export default { 13 mounted() {}, 14 data(){ 15 return { 16 renderTarget: null, 17 height: 800, 18 width: 800, 19 radius: 3.33, 20 charge: -5.11, 21 svg: null, 22 } 23 }, 24 computed: { 25 mapData(){ 26 let nodes = [], links = [] 27 let useMap = (taskId) => this.$store.state.tasks[this.$store.state.hashMap[taskId]] 28 let contextMemberId = this.$store.getters.contextMember.memberId 29 if (contextMemberId) { 30 this.$store.state.tasks 31 .filter(t => t.deck.indexOf(contextMemberId) > -1) 32 .map((t) => { 33 return { 34 id: t.taskId, 35 } 36 }) 37 .forEach(x => nodes.push(x)) 38 nodes.push({ 39 id: contextMemberId, 40 }) 41 } else { 42 contextMemberId = this.$store.getters.member.memberId 43 let contextTaskId = this.$store.getters.contextCard.taskId 44 let localcontents = calculations.crawler(this.$store.state.tasks, contextTaskId) 45 this.$store.state.tasks.forEach( t => { 46 if (t.subTasks.indexOf(contextTaskId) > -1 || t.priorities.indexOf(contextTaskId) > -1 || t.completed.indexOf(contextTaskId) > 1){ 47 localcontents.push(t.taskId) 48 } 49 }) 50 localcontents.push(contextTaskId) 51 new Set(localcontents).forEach(taskId => nodes.push({id: taskId})) 52 } 53 nodes.forEach(n => { 54 let t = useMap(n.id) 55 let source = n.id 56 let subs = t.subTasks.concat(t.priorities).concat(t.completed) 57 subs.forEach(target => { 58 links.push({ 59 source, 60 target 61 }) 62 }) 63 }) 64 links.forEach(l => nodes.push({id: l.target})) 65 let uniqNodes = [...new Set(nodes.map(n => n.id))] 66 nodes = uniqNodes.map(taskId => { 67 return {"id": taskId} 68 }) 69 return { nodes, links } 70 }, 71 }, 72 methods: { 73 drawVis() { 74 document.getElementById("my_dataviz").innerHTML = ""; 75 this.renderTarget = this.$store.getters.contextCard.taskId 76 this.svg = d3.select("#my_dataviz") 77 .append("svg") 78 .attr("width", this.width) 79 .attr("height", this.height) 80 .append("g") 81 var data = this.mapData 82 console.log("drawing map with" , data.nodes.length, "nodes &", data.links.length, "links") 83 let useMap = (taskId) => this.$store.state.tasks[this.$store.state.hashMap[taskId]] 84 85 var link = this.svg 86 .selectAll("line") 87 .data(data.links) 88 .enter() 89 .append("line") 90 .style("stroke", d => { 91 let t = useMap(d.source) 92 if (t.priorities.indexOf(d.target) > -1) return "#fcccd5" 93 if (t.completed.indexOf(d.target) > -1) return "#abbcff" 94 return "#E4F1F2" 95 }) 96 var node = this.svg 97 .selectAll("circle") 98 .data(data.nodes) 99 .enter() 100 .append("circle") 101 .attr("r", d => { 102 if (d.id === this.$store.getters.contextCard.taskId) return this.radius * 3.3 103 return this.radius 104 }) 105 .attr("data", d => { 106 return d.id 107 }) 108 .style("fill", d => { 109 let card = useMap(d.id) 110 if (card.deck.indexOf(this.$store.getters.member.memberId) === -1){ 111 return "#E4F1F2" 112 } 113 switch(card.color){ 114 case "red": return "#ffb3b1"; 115 case "yellow": return "#FFFF99"; 116 case "green": return "#b3ffb3"; 117 case "purple": return "#b39ef7"; 118 case "blue": return "#79aeff"; 119 } 120 }) 121 .style("cursor", "pointer") 122 .on("mouseover", d => { 123 d3.select(d.target) 124 .transition() 125 .duration(100) 126 .attr("r", 20) 127 }) 128 .on("mouseout", d => { 129 d3.select(d.target).transition() 130 .duration(1111) 131 .attr("r", this.radius) 132 }) 133 .on("click", d => { 134 let taskId = d.target.getAttribute("data") 135 this.$store.commit("rollStackPush", taskId) 136 }) 137 // don't get how this is supposed to work (thinking with joins) 138 //.exit().remove() 139 140 d3.forceSimulation(data.nodes) 141 .force("boundary", forceBoundary(this.radius, this.radius , this.width - this.radius, this.height - this.radius)) 142 .force("link", d3.forceLink().id( d => d.id).links(data.links)) 143 .force("charge", d3.forceManyBody().strength(this.charge)) 144 .force("center", d3.forceCenter(this.width / 2, 200).strength(1.337)) 145 .force("collision", d3.forceCollide( d => { 146 if (d.id === this.$store.getters.contextCard.taskId) return this.radius * 3.3 147 return this.radius + 1.77 148 })) 149 .on("end", () => { 150 link 151 .attr("x1", d => d.source.x) 152 .attr("y1", d => d.source.y) 153 .attr("x2", d => d.target.x) 154 .attr("y2", d => d.target.y); 155 156 node 157 .attr("cx", d => d.x + 1) 158 .attr("cy", d => d.y - 1); 159 }) 160 } 161 } 162 } 163 </script> 164 165 <style lang="stylus" scoped> 166 167 @import '../styles/colours' 168 @import '../styles/button' 169 #my_dataviz 170 min-height:100px 171 .datavisbox 172 display: none; 173 position: relative 174 text-align: center 175 min-height: 600px 176 177 label 178 color: lightGrey 179 button 180 margin-right: 2.4em 181 max-width: 17em 182 pointer-events: all; 183 opacity: 0.4 184 margin-bottom:6em 185 186 </style>