|
@@ -1,68 +1,44 @@
|
|
|
<template>
|
|
|
- <b-overlay :show="texts === undefined" class="h-100">
|
|
|
- <svg
|
|
|
- width="100%" height="100%"
|
|
|
- ref="svg" id="vis"
|
|
|
- :view-box.camel="viewBox"
|
|
|
- >
|
|
|
- <g id="cards">
|
|
|
- <foreignObject
|
|
|
- v-for="(node, i) in nodes" :key="i"
|
|
|
- width="500" height="300"
|
|
|
- :x="node.x" :y="node.y"
|
|
|
- :transform="`rotate(${node.rotate})`"
|
|
|
- >
|
|
|
- <node-view
|
|
|
- :node="texts[i]"
|
|
|
- mode="card"
|
|
|
- @open-node="onOpen(texts[i])"
|
|
|
- />
|
|
|
- </foreignObject>
|
|
|
- </g>
|
|
|
- </svg>
|
|
|
+ <b-overlay :show="nodes === undefined" class="h-100">
|
|
|
+ <map-zoom id="library-map" :min-zoom="0.2" :max-zoom="2">
|
|
|
+ <foreignObject
|
|
|
+ v-for="(node, i) in nodes" :key="i"
|
|
|
+ :style="`--x: ${node.x}px; --y: ${node.y}px; --r: ${node.rotate}deg;`"
|
|
|
+ >
|
|
|
+ <node-view
|
|
|
+ :node="node.data"
|
|
|
+ mode="card"
|
|
|
+ @open-node="onOpen(node.data)"
|
|
|
+ />
|
|
|
+ </foreignObject>
|
|
|
+ </map-zoom>
|
|
|
</b-overlay>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { mapGetters } from 'vuex'
|
|
|
-
|
|
|
-import { select, randomUniform, zoom } from 'd3'
|
|
|
-import {
|
|
|
- forceSimulation, forceCenter, forceCollide
|
|
|
-} from 'd3-force'
|
|
|
-
|
|
|
+import { randomUniform } from 'd3'
|
|
|
+import { forceSimulation, forceCollide, forceManyBody } from 'd3-force'
|
|
|
|
|
|
+import { MapZoom } from '@/components/layouts'
|
|
|
import { NodeView } from '@/components/nodes'
|
|
|
|
|
|
|
|
|
export default {
|
|
|
- name: 'CardMap',
|
|
|
+ name: 'LibraryMap',
|
|
|
|
|
|
components: {
|
|
|
+ MapZoom,
|
|
|
NodeView
|
|
|
},
|
|
|
|
|
|
data () {
|
|
|
return {
|
|
|
- width: 100,
|
|
|
- height: 100,
|
|
|
- nodes: undefined,
|
|
|
simulation: forceSimulation(),
|
|
|
- viewBox: null,
|
|
|
- texts: undefined
|
|
|
+ nodes: undefined
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- computed: {
|
|
|
- ...mapGetters(['textsDepart'])
|
|
|
- },
|
|
|
-
|
|
|
methods: {
|
|
|
- updateSize () {
|
|
|
- const { width, height } = this.$refs.svg.getBoundingClientRect()
|
|
|
- Object.assign(this.$data, { width, height })
|
|
|
- },
|
|
|
-
|
|
|
onOpen (node) {
|
|
|
// FIXME ALSO CHECK IF PARENT
|
|
|
this.$emit('open-node', node.id)
|
|
@@ -70,32 +46,13 @@ export default {
|
|
|
},
|
|
|
|
|
|
created () {
|
|
|
- this.$store.dispatch('INIT_LIBRARY_MAP').then(data => {
|
|
|
- this.texts = data
|
|
|
- this.nodes = data.map((node, i) => {
|
|
|
- return { x: i, rotate: randomUniform(-25, 25)() }
|
|
|
- })
|
|
|
-
|
|
|
- this.$nextTick(() => {
|
|
|
- this.updateSize()
|
|
|
- this.viewBox = `0 0 ${this.width} ${this.height}`
|
|
|
- const svg = select('#vis')
|
|
|
- const g = select('#cards')
|
|
|
- svg.call(zoom()
|
|
|
- .extent([[0, 0], [this.width, this.height]])
|
|
|
- .scaleExtent([-5, 8])
|
|
|
- .on('zoom', zoomed))
|
|
|
-
|
|
|
- function zoomed ({ transform }) {
|
|
|
- g.attr('transform', transform)
|
|
|
- }
|
|
|
-
|
|
|
- this.simulation
|
|
|
- .nodes(this.nodes)
|
|
|
- .force('collide', forceCollide(d => randomUniform(250, 350)()))
|
|
|
- .force('center', forceCenter(this.width * 0.5, this.height * 0.5))
|
|
|
- .alphaDecay([0.02])
|
|
|
+ this.$store.dispatch('INIT_LIBRARY_MAP').then(nodes => {
|
|
|
+ this.nodes = nodes.map((node, i) => {
|
|
|
+ return { x: i, rotate: randomUniform(-25, 25)(), data: node }
|
|
|
})
|
|
|
+ this.simulation.nodes(this.nodes)
|
|
|
+ .force('attract', forceManyBody().strength(10))
|
|
|
+ .force('collision', forceCollide().radius(650 / 2))
|
|
|
})
|
|
|
}
|
|
|
}
|
|
@@ -103,6 +60,16 @@ export default {
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
foreignObject {
|
|
|
- transform-origin: 250, 150;
|
|
|
+ width: $node-card-width;
|
|
|
+ height: $node-card-height;
|
|
|
+ x: var(--x);
|
|
|
+ y: var(--y);
|
|
|
+ transform-origin: var(--x) var(--y);
|
|
|
+ transform: rotate(var(--r)) translate(-#{$node-card-width / 2}, -#{$node-card-height / 2});
|
|
|
+ overflow: visible;
|
|
|
+
|
|
|
+ .node-view {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|