/ src / components / DataVis.vue
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>