Commit 8184f9b8 authored by Marko Řeháček's avatar Marko Řeháček
Browse files

Replace GraphNode/Link with very simple functional components, with no...

Replace GraphNode/Link with very simple functional components, with no interactivity or store/data connections.
parent b0e3e2a7
Loading
Loading
Loading
Loading
+19 −51
Original line number Diff line number Diff line
@@ -45,35 +45,15 @@

      <rect id="zoom-pan-interaction-layer" width="100%" height="100%" fill-opacity="0" />

      <g id="g-elements">
        <g
          class="g-links"
      <g id="g-elements"
          :transform="
            `translate(${graph.transform.x}, ${graph.transform.y}) 
               scale(${graph.transform.k})`
          "
        >
          <!-- when running force simulation, show a dumbed down version of links due to performance -->
          <transition name="fade-quick">
            <g v-if="graph.showTemporaryLinks && data.links.length < 100">
              <line
                v-for="link in data.links"
                :key="`temp-link-${link.id}`"
                :x1="link.source.x"
                :y1="link.source.y"
                :x2="link.target.x"
                :y2="link.target.y"
                class="temporary-link"
              />
            </g>
          </transition>

          <transition name="fade">
            <g v-if="!isSimulationRunning">
              <GraphLink
        <GraphLinkFunctional
                v-for="link in data.links"
                :key="`link-${link.id}`"
                :dataId="link.id"
                :source="{
                  id: link.source.id,
                  x: link.source.x,
@@ -84,30 +64,14 @@
                  x: link.target.x,
                  y: link.target.y
                }"
                :opacity="link.opacity"
                :visuals="visuals"
              />
            </g>
          </transition>
        </g>

        <g
          class="g-nodes"
          :transform="
            `translate(${graph.transform.x}, ${graph.transform.y}) 
               scale(${graph.transform.k})`
          "
        >
          <GraphNode
        <GraphNodeFunctional
            v-for="node in data.nodes"
            :key="`node-${node.id}`"
            :dataId="node.id"
          :node="{ type: 'regular', dataClass: 'person', id: 0, label: 'abc'}"
            :visuals="visuals"
            :graphX="node.x"
            :graphY="node.y"
            :opacity="node.opacity"
            :moveCallback="moveNode"
            @node-mouseout="saveNodePosition(node.id, node.x, node.y)"
          />

          <!-- Moving a node shall render it on top, ideally reordering the DOM
@@ -116,26 +80,25 @@
          moving another node, but it is acceptable.-->
          <use v-if="graph.lastMovedNodeId" :xlink:href="`#node-${graph.lastMovedNodeId}`" />
        </g>
      </g>
    </svg>
  </div>
</template>

<script>
import Vue from 'vue'
import { mapActions, mapState } from 'vuex'
import { mapActions, mapState, mapGetters } from 'vuex'
import * as d3 from 'd3' // TODO: import only needed modules
import VueScreenSize from 'vue-screen-size'

import { sleep, getLastElementByIndex } from '@/mixins/helpers.js'
import GraphNode from '@/components/NetworkGraph/GraphNode.vue'
import GraphLink from '@/components/NetworkGraph/GraphLink.vue'
import GraphNodeFunctional from '@/components/NetworkGraph/GraphNodeFunctional.vue'
import GraphLinkFunctional from '@/components/NetworkGraph/GraphLinkFunctional.vue'
import GraphLinkMarker from '@/components/NetworkGraph/GraphLinkMarker.vue'

export default {
  components: {
    GraphNode,
    GraphLink,
    GraphNodeFunctional,
    GraphLinkFunctional,
    GraphLinkMarker
  },

@@ -184,7 +147,7 @@ export default {
        width: null,
        height: null,
        transform: { x: 0, y: 0, k: 1 }, // set by d3-zoom
        zoomMin: 0.2,
        zoomMin: 0.05,
        zoomMax: 5,
        isPanningOrZooming: false,
        lastMovedNodeId: NaN, // to maintain moving node on top in DOM
@@ -201,7 +164,8 @@ export default {
  },

  computed: {
    ...mapState('interaction', ['inDebug'])
    ...mapState('interaction', ['inDebug']),
    ...mapState('data', ['nodes'])
  },

  watch: {
@@ -270,6 +234,10 @@ export default {
    ]),
    ...mapActions('data', ['updateNodes']),

    getNodeById(id) {
      return this.nodes.find(n => n.id === id)
    },

    respondAddNode(payload) {
      console.log('NetworkGraph: data/ADD_NODE with payload:' + JSON.stringify(payload))
      this.data.nodes.push({
@@ -452,9 +420,9 @@ export default {
          'charge',
          d3
            .forceManyBody()
            .strength(-2000)
            .distanceMin(50)
            .distanceMax(400)
            .strength(-20)
            .distanceMin(5)
            .distanceMax(40)
        ) // electrostatic force between all nodes
        .force('center', d3.forceCenter(this.graph.width / 2, this.graph.height / 2)) // translate to svg center
        .force(
+86 −0
Original line number Diff line number Diff line
<template functional>
  <g class="link">
    <line
      :x1="props.source.x"
      :y1="props.source.y"
      :x2="props.target.x"
      :y2="props.target.y"
      class="link-line"
    ></line>

    <line
      :x1="props.source.x"
      :y1="props.source.y"
      :x2="props.target.x"
      :y2="props.target.y"
      class="support-link"
    ></line>
  </g>
</template>

<style lang="sass" scoped>
.link
  line
    cursor: pointer
    stroke: #bbb
    &.with-arrows
      marker-end: url(#single-link-arrow)
    &.multilink
      stroke-width: 3px
      stroke: #ccc
      &.source-to-target
        marker-end: url(#multi-link-arrow)
        &.non-directional
          marker-end: url(#multi-link-arrow-bar)
      &.target-to-source
        marker-start: url(#multi-link-arrow-opposite)
        &.non-directional
          marker-start: url(#multi-link-arrow-bar-opposite)
    &.user-defined
      stroke-dasharray: 7,7
      stroke-linecap: round
    &.transferred
      stroke-dasharray: 0,6
      stroke-width: 2px
      stroke-linecap: round
      &.multilink
        stroke-width: 4px
    &.highlight.highlightable
      stroke: #444
  line.hovered
    stroke: #757575

.link.detailed line.link-line
    stroke: #757575

.link.highlighted line.link-line
    stroke: #757575

.support-link
  stroke-opacity: 0
  stroke-width: 16px

.link-label-box
  display: inline
  fill: rgba(220, 220, 220, 0.7)

text.label-id
  font-size: 11pt
  font-weight: 600
  fill: #4903fc
  user-select: none
text.multilink-label
  font-size: 0.8em
  font-weight: 600
  fill: #999

.link-paws
  circle
    fill: #bbb
  .detailed
    fill: #444
  .hovered
    fill: #757575
  .highlighted
    fill: #757575
</style>
+112 −0
Original line number Diff line number Diff line
<template functional>
  <g class="node" :transform="`translate(${props.graphX}, ${props.graphY})`">
    <g class="node-label">
      <rect
        class="node-label-box"
        :width="150"
        height="22"
        rx="5"
        ry="5"
        y="-12"
        :x="props.node.type == 'group' ? props.visuals.groupNodeRadiusDifference / 2.0 : 0"
      />
      <text
        class="node-label-text"
        dx="24"
        dy="4"
        :x="props.node.type == 'group' ? props.visuals.groupNodeRadiusDifference : 0"
      >
        {{ props.node.label.length >= 18 ? props.node.label.substring(0, 15) + '...' : props.node.label }}
      </text>
    </g>

    <g class="node-icon">
      <circle
        v-if="props.node.type === 'group'"
        class="node-circle"
        :r="20 + props.visuals.groupNodeRadiusDifference"
      />

      <!-- alias -->
      <circle
        v-if="props.node.type === 'alias'"
        class="node-circle"
        :cx="-20 / 2.0"
        :cy="-20 / 2.0"
        :r="20"
      />

      <circle class="node-circle" :r="20" :fill="`#fff`" />
      <BaseIcon
        use-svg
        :name="props.node.type === 'group' ? 'group' : props.node.dataClass"
        :radius="20"
      />
    </g>
  </g>
</template>

<style lang="sass" scoped>
.node-icon
  cursor: pointer

.hovered .node-circle
  stroke: #979797

.node-circle
  stroke-width: 1.5px
  stroke: #d8d8d8
  fill: #fff

.node.removed
  .node-circle
    stroke-dasharray: 4
    image
      fill: red

.node-label-box
  display: inline
  fill: rgba(220, 220, 220, 0.65) // #f2f2f2

.node-label-text
  font-size: 0.85em
  fill: #666
  display: inline

.node-label-id
  font-size: 1.3em
  font-weight: 600
  fill: #4903fc
  user-select: none

.detailed .node-icon .node-circle
  stroke: #757575

.highlighted .node-icon .node-circle
  stroke: #979797
  fill: #979797

.selected .node-icon .node-circle
  stroke: #1e2dff
  stroke-width: 1.5px

.node-circle-detailed-and-selected
  stroke: #979797
  stroke-width: 5px

.node-circle.connected-link-hovered
  stroke: #757575

.bundle .bundle-bubble
  display: inline

.bundle-circle
  stroke-width: 1.5px
  stroke: #D8D8D8
  fill: #fff

.bundle-text
  font-size: 0.65em
  font-weight: 600
  fill: #666
</style>
+2 −2
Original line number Diff line number Diff line
@@ -27,8 +27,8 @@ export function generateDataset(nodeCount, linkCount) {
    }
    if (insertLinkWithMultilinkUpdate(link, links, n + 1)) n++

    if (random(0, 100) < 40) sourceId++
    if (random(0, 100) < 40) targetId--
    if (random(0, 100) < 50) sourceId++
    if (random(0, 100) < 50) targetId--
    if (sourceId === targetId) sourceId++
    if (sourceId > lastNodeId || targetId > lastNodeId || targetId < 0) break
  }