|
@@ -1,5 +1,8 @@
|
|
|
<template>
|
|
|
- <svg width="100%" height="100%" ref="svg">
|
|
|
+ <svg
|
|
|
+ width="100%" height="100%"
|
|
|
+ ref="svg" :id="id"
|
|
|
+ >
|
|
|
<path
|
|
|
v-for="(item, index) in links"
|
|
|
:key="`link_${index}`"
|
|
@@ -7,21 +10,22 @@
|
|
|
stroke="black"
|
|
|
/>
|
|
|
|
|
|
- <circle
|
|
|
- v-for="(node, index) in nodes"
|
|
|
- :key="index"
|
|
|
- :cx="node.x"
|
|
|
- :cy="node.y"
|
|
|
- :class="node.data.class"
|
|
|
- :r="node.children ? 7 : 5"
|
|
|
- >
|
|
|
- <title>{{ node.data.name }}</title>
|
|
|
- </circle>
|
|
|
+ <template v-for="(node, index) in nodes">
|
|
|
+ <circle
|
|
|
+ :key="'circle' + index"
|
|
|
+ :cx="node.x"
|
|
|
+ :cy="node.y"
|
|
|
+ :class="node.data.class + (node.data.first ? ' first' : '')"
|
|
|
+ :r="node.children ? 7 : 5"
|
|
|
+ >
|
|
|
+ <title>{{ node.data.title }}</title>
|
|
|
+ </circle>
|
|
|
+ <text :key="'text' + index" :x="node.x + 7" :y="node.y-7">{{ node.data.id }}</text>
|
|
|
+ </template>
|
|
|
</svg>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { hierarchy } from 'd3-hierarchy'
|
|
|
import { line } from 'd3-shape'
|
|
|
import { selectAll } from 'd3-selection'
|
|
|
import { drag } from 'd3-drag'
|
|
@@ -39,29 +43,20 @@ export default {
|
|
|
name: 'NodeMap',
|
|
|
|
|
|
props: {
|
|
|
- data: { type: Object, required: true }
|
|
|
+ nodes: { type: Array, required: true },
|
|
|
+ links: { type: Array, required: true },
|
|
|
+ id: { type: String, default: 'map' }
|
|
|
},
|
|
|
|
|
|
data () {
|
|
|
return {
|
|
|
width: 100,
|
|
|
height: 100,
|
|
|
- h: hierarchy({}),
|
|
|
simulation: forceSimulation(),
|
|
|
lineGenerator: line().x(node => node.x).y(node => node.y)
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- computed: {
|
|
|
- nodes () {
|
|
|
- return this.h.descendants()
|
|
|
- },
|
|
|
-
|
|
|
- links () {
|
|
|
- return this.h.links()
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
methods: {
|
|
|
updateSize () {
|
|
|
const { width, height } = this.$el.getBoundingClientRect()
|
|
@@ -71,16 +66,11 @@ export default {
|
|
|
init () {
|
|
|
this.updateSize()
|
|
|
|
|
|
- const h = hierarchy(this.data)
|
|
|
+ this.nodes.forEach(node => {
|
|
|
+ this.$set(node, 'x', this.width * 0.5)
|
|
|
+ this.$set(node, 'y', this.height * 0.5)
|
|
|
+ })
|
|
|
|
|
|
- h.descendants().forEach(node =>
|
|
|
- Object.assign(node, {
|
|
|
- x: this.width * 0.5,
|
|
|
- y: this.height * 0.5
|
|
|
- })
|
|
|
- )
|
|
|
-
|
|
|
- this.h = h
|
|
|
this.setupForces()
|
|
|
// Wait for DOM update so d3 can select
|
|
|
this.$nextTick(this.initListeners)
|
|
@@ -88,7 +78,8 @@ export default {
|
|
|
|
|
|
initListeners () {
|
|
|
// TODO replace with vue events ?
|
|
|
- selectAll('circle')
|
|
|
+
|
|
|
+ selectAll(`#${this.id} circle`)
|
|
|
.data(this.nodes)
|
|
|
.call(drag()
|
|
|
.on('start', this.onNodeDragStart)
|
|
@@ -100,8 +91,8 @@ export default {
|
|
|
setupForces () {
|
|
|
this.simulation
|
|
|
.nodes(this.nodes)
|
|
|
- .force('link', forceLink(this.links).distance(100))
|
|
|
- .force('charge', forceManyBody().strength(-350))
|
|
|
+ .force('link', forceLink(this.links).id(d => d.id).distance(50))
|
|
|
+ .force('charge', forceManyBody().strength(-150))
|
|
|
.force('x', forceX())
|
|
|
.force('y', forceY())
|
|
|
.force('center', forceCenter(this.width * 0.5, this.height * 0.5))
|
|
@@ -142,6 +133,11 @@ path {
|
|
|
stroke: grey;
|
|
|
}
|
|
|
|
|
|
+text {
|
|
|
+ font-size: 0.7rem;
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+
|
|
|
@each $id in map-keys($families){
|
|
|
.family-#{$id} {
|
|
|
fill: setColorFromId($id);
|