update LibraryTree with MapZoom

This commit is contained in:
axolotle
2021-06-11 20:40:28 +02:00
parent 56e1c81aef
commit cfee58bde7
4 changed files with 148 additions and 16 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
<strong class="d-block">
{{ toCommaList(node.authors) }},
</strong>
<span v-if="node.preTitle" v-html="node.preTitle + ','" />
<span v-if="node.preTitle" v-html="trim(node.preTitle) + ','" />
<em v-html="(trim(node.title) || 'pas de titre ital') + ','" />
<div v-if="node.edition" class="edition">
{{ node.edition.map(ed => ed.name).join(' ') }}
+143 -12
View File
@@ -1,35 +1,112 @@
<template>
<b-overlay :show="nodeTree === undefined" class="h-100">
<node-map
v-if="nodeTree !== undefined"
v-bind="nodeTree"
v-on="$listeners"
/>
<b-overlay :show="nodeTree.nodes.length === 0" class="h-100">
<map-zoom id="library-tree" :min-zoom="0.3" :max-zoom="1">
<path
v-for="link in nodeTree.links"
:key="`${link.source.data.id}_${link.target.data.id}`"
:d="lineGenerator([link.source, link.target])"
:class="['svg-link', (link.linkType || link.target.data.linkType)]"
/>
<circle
v-for="node in nodeTree.nodes"
:key="'circle' + node.data.id"
:cx="node.x"
:cy="node.y"
class="svg-dot"
:class="['svg-dot-' + node.data.variant, { 'origin': node.parent === null }]"
tabindex="0"
@click="onNodeClick(node.data)"
@keyup.enter="onNodeClick(node.data)"
@mouseenter="activeNode = node"
/>
<g>
<rect class="svg-overlay" />
<foreignObject
v-if="activeNode"
:x="activeNode.x"
:y="activeNode.y"
>
<dot-button
:variant="activeNode.data.variant"
class="active"
@click="onNodeClick(activeNode.data)"
@mouseleave="activeNode = null"
>
<template v-if="activeNode.data.variant !== 'depart'">
{{ $t('variants.' + activeNode.data.variant) }}<br>
</template>
{{ toCommaList(activeNode.data.authors) }},<br>
<span v-if="activeNode.data.preTitle" v-html="trim(activeNode.data.preTitle) + ','" />
<span v-html="trim(activeNode.data.title) || '<em>pas de titre ital</em>'" />
</dot-button>
</foreignObject>
</g>
</map-zoom>
</b-overlay>
</template>
<script>
import { mapGetters } from 'vuex'
import { line } from 'd3-shape'
import { forceSimulation, forceLink, forceManyBody, forceX, forceY } from 'd3-force'
import NodeMap from '@/components/NodeMap'
import { trim, toCommaList } from '@/helpers/common'
import { MapZoom } from '@/components/layouts'
export default {
name: 'TreeMap',
name: 'LibraryTree',
components: {
NodeMap
MapZoom
},
data () {
return {
loading: true,
mapData: undefined
activeNode: null
}
},
computed: {
...mapGetters(['nodeTree'])
...mapGetters(['nodeTree']),
// ONE TIME GETTER
simulation () {
return forceSimulation()
.force('charge', forceManyBody().strength((d) => {
if (d.data.linkType === 'parents') return -2000
if (d.data.linkType === 'siblings') return -500
return -1000
}))
.force('link', forceLink().id(d => d.id).distance((d) => {
if (d.linkType === 'parents') return 200
if (d.linkType === 'siblings') return 100
return 0
}).strength(0.5))
.force('x', forceX())
.force('y', forceY())
},
// ONE TIME GETTER
lineGenerator () {
return line().x(node => node.x).y(node => node.y)
}
},
watch: {
nodeTree (tree) {
this.activeNode = null
this.simulation.nodes(tree.nodes)
this.simulation.force('link').links(tree.links)
this.simulation.alpha(0.5).restart()
}
},
methods: {
toCommaList,
trim
},
created () {
@@ -39,4 +116,58 @@ export default {
</script>
<style lang="scss" scoped>
.svg {
&-link {
stroke: grey;
vector-effect: non-scaling-stroke;
&.siblings {
stroke-dasharray: 4;
}
&.parents {
stroke: red;
opacity: .3;
}
}
&-dot {
r: 9.5px;
@each $color, $value in $theme-colors {
&-#{$color} {
fill: $value;
@if $color == 'depart' {
stroke: $black;
stroke-width: 3px;
vector-effect: non-scaling-stroke;
}
}
}
&.origin {
r: 15px;
}
}
&-overlay {
width: 100%;
height: 100%;
x: -50%;
y: -50%;
fill: transparent;
pointer-events: none;
}
}
foreignObject {
height: 1px;
width: 1px;
overflow: visible;
}
.dot-btn {
transform: translate(-50%, -50%);
}
</style>
+2 -3
View File
@@ -177,7 +177,7 @@ export default {
})
},
activeNodes: (state) => {
activeNodes: state => {
if (!state.nodebook) return []
return state.nodebook.reduce((acc, ids) => [...acc, ...ids], [])
},
@@ -218,8 +218,7 @@ export default {
},
nodeTree: (state, rootState) => {
if (state.nodeDepartId === undefined) return
return state.trees[state.nodeDepartId]
return state.trees[state.nodeDepartId] || { nodes: [], links: [] }
}
}
}
+2
View File
@@ -92,6 +92,8 @@ export function buildTree (treeData, nodesData) {
if (!uniqueIds.includes(node.id)) {
uniqueIds.push(node.id)
node.data = nodesData.find(n => n.id === node.id)
// Add `x` and `y` keys so Vue can update on these values updates
Object.assign(node, { x: undefined, y: undefined })
nodes.push(node)
if (node.children) {
node.children.forEach(child => {