180 lines
4.3 KiB
Vue
180 lines
4.3 KiB
Vue
<template>
|
|
<b-overlay :show="nodes === undefined" class="h-100" z-index="0">
|
|
<legend-toggle>
|
|
<h6>Mode Aléatoire</h6>
|
|
<p>Ce mode vous propose 3 textes de départ tirés aléatoirement ainsi que leurs textes produits et leurs textes rebonds.</p>
|
|
<p>Pour tirer aléatoirement 3 nouveaux textes, cliquez sur <i>Mélanger</i> dans le menu d'options.</p>
|
|
</legend-toggle>
|
|
|
|
<map-zoom
|
|
id="library-map" ref="map"
|
|
:min-zoom="0.2" :max-zoom="2" :initial-zoom="0.5"
|
|
>
|
|
<foreignObject
|
|
v-for="node in filteredNodes" :key="'f-' + node.data.id"
|
|
:style="`--x: ${node.x}px; --y: ${node.y}px; --r: ${node.rotate}deg;`"
|
|
>
|
|
<node-view
|
|
:id="'preview-node-' + node.data.id"
|
|
:class="{ 'hidden': previewNode === node.data }"
|
|
:node="node.data"
|
|
mode="card"
|
|
:hidden="node.hidden"
|
|
@click.native="onNodeClick(node.data)"
|
|
:style="`--opacity: ${getStrangenessOpacity(strangeness, node.data)};`"
|
|
/>
|
|
</foreignObject>
|
|
</map-zoom>
|
|
|
|
<node-preview-zone
|
|
v-if="dataNodes"
|
|
v-model="previewNode" :nodes="dataNodes"
|
|
@open-node="onPreviewNodeClick"
|
|
/>
|
|
</b-overlay>
|
|
</template>
|
|
|
|
<script>
|
|
import { randomUniform } from 'd3'
|
|
import { forceSimulation, forceCollide, forceManyBody } from 'd3-force'
|
|
import { mapGetters } from 'vuex'
|
|
|
|
import { searchInNode, tagsInNode, getRelation, getStrangenessOpacity } from '@/store/utils'
|
|
import { MapZoom, NodePreviewZone } from '@/components/layouts'
|
|
import { NodeView } from '@/components/nodes'
|
|
|
|
|
|
export default {
|
|
name: 'LibraryMap',
|
|
|
|
components: {
|
|
MapZoom,
|
|
NodePreviewZone,
|
|
NodeView
|
|
},
|
|
|
|
data () {
|
|
return {
|
|
simulation: forceSimulation(),
|
|
nodes: undefined,
|
|
previewNode: null
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters(['randomNodes', 'search', 'tags', 'strangeness']),
|
|
|
|
filteredNodes () {
|
|
if (!this.nodes) return
|
|
const search = this.search.toLowerCase()
|
|
this.nodes.forEach(node => {
|
|
Object.assign(node, {
|
|
hidden: !(tagsInNode(this.tags, node.data.tags) && searchInNode(search, node.data))
|
|
})
|
|
})
|
|
return this.nodes
|
|
},
|
|
|
|
dataNodes () {
|
|
if (!this.nodes) return
|
|
return this.nodes.map(node => node.data)
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
getStrangenessOpacity,
|
|
|
|
onOpen (node) {
|
|
this.$parent.$emit('open-node', getRelation(node))
|
|
},
|
|
|
|
onNodeClick (node) {
|
|
this.previewNode = node
|
|
this.$root.$emit('bv::show::popover', 'preview-node-' + node.id)
|
|
},
|
|
|
|
onPreviewNodeClick (ids) {
|
|
this.$root.$emit('bv::hide::popover', 'preview-node-' + this.previewNode.id)
|
|
this.$emit('open-node', ids)
|
|
this.previewNode = null
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
randomNodes (randomNodes) {
|
|
if (!randomNodes) {
|
|
this.nodes = undefined
|
|
return
|
|
}
|
|
|
|
// JS-BP
|
|
const radius = window.innerWidth < 769 ? 700 / 1.5 : 700
|
|
this.nodes = randomNodes.map((node, i) => {
|
|
return { x: 0, rotate: randomUniform(-25, 25)(), data: node }
|
|
})
|
|
|
|
this.simulation.nodes(this.nodes)
|
|
.force('attract', forceManyBody().strength(1))
|
|
.force('collision', forceCollide().radius(radius / 2))
|
|
.alpha(0.5).restart()
|
|
|
|
this.$refs.map.reset()
|
|
}
|
|
},
|
|
|
|
created () {
|
|
this.$store.dispatch('INIT_LIBRARY_MAP')
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
#library-map {
|
|
foreignObject {
|
|
width: $node-card-width-sm;
|
|
height: $node-card-height-sm;
|
|
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;
|
|
|
|
@include media-breakpoint-up($size-bp) {
|
|
width: $node-card-width;
|
|
height: $node-card-height;
|
|
}
|
|
|
|
.node-view {
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
.node-view {
|
|
width: $node-card-width-sm;
|
|
opacity: var(--opacity);
|
|
|
|
@include media-breakpoint-up($size-bp) {
|
|
width: $node-card-width;
|
|
}
|
|
|
|
&-wrapper {
|
|
height: $node-card-height-sm;
|
|
|
|
@include media-breakpoint-up($size-bp) {
|
|
height: $node-card-height;
|
|
}
|
|
}
|
|
|
|
&.hidden {
|
|
border: $line;
|
|
border-style: dashed;
|
|
background-color: transparent;
|
|
|
|
.node-view-wrapper {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|