130 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
    <div>
 | 
						|
      <select v-model="selectedGroup" @change="focusGroup">
 | 
						|
        <option v-for="g in groups" :key="g" :value="g">
 | 
						|
          Group {{ g }}
 | 
						|
        </option>
 | 
						|
      </select>
 | 
						|
  
 | 
						|
      <div ref="cyEl" style="width: 100%; height: 800px;"></div>
 | 
						|
    </div>
 | 
						|
  </template>
 | 
						|
  
 | 
						|
  <script setup>
 | 
						|
  import { ref, onMounted } from 'vue';
 | 
						|
  import cytoscape from "cytoscape";
 | 
						|
  
 | 
						|
  let CytoscapeOverlays = null
 | 
						|
  
 | 
						|
  if (import.meta.client) {
 | 
						|
    const useOverlay = (await import('@/composables/useOverlay')).default
 | 
						|
    CytoscapeOverlays = await useOverlay()
 | 
						|
  }
 | 
						|
  
 | 
						|
  useHead({
 | 
						|
    script: [
 | 
						|
      {
 | 
						|
        src: '/dist/cy_custom.js',
 | 
						|
        defer: true
 | 
						|
      }
 | 
						|
    ]
 | 
						|
  });
 | 
						|
  
 | 
						|
  const cyEl = ref(null);
 | 
						|
  const cy = ref(null);
 | 
						|
  const selectedGroup = ref(null);
 | 
						|
  const groups = ref([]);
 | 
						|
  
 | 
						|
  onMounted(() => {
 | 
						|
    fetch("/group43200.json")
 | 
						|
      .then(res => res.json())
 | 
						|
      .then(data => {
 | 
						|
        const nodes = data.entries.map(e => ({
 | 
						|
          data: { id: `n${e.id}`, label: e.graphics.name, group: e.group },
 | 
						|
          position: { x: e.graphics.x, y: e.graphics.y },
 | 
						|
        }));
 | 
						|
        const edges = data.relations.map(r => ({
 | 
						|
          data: {
 | 
						|
            source: `n${r.entry1}`,
 | 
						|
            target: `n${r.entry2}`,
 | 
						|
          }
 | 
						|
        }));
 | 
						|
        groups.value = [...new Set(data.entries.map(e => e.group))];
 | 
						|
  
 | 
						|
        const colorPalette = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080'];
 | 
						|
        const groupColorMap = {};
 | 
						|
        groups.value.forEach((group, index) => {
 | 
						|
          groupColorMap[group] = colorPalette[index % colorPalette.length];
 | 
						|
        });
 | 
						|
 | 
						|
        cy.value = cytoscape({
 | 
						|
          container: cyEl.value,
 | 
						|
          elements: [...nodes, ...edges],
 | 
						|
          style: [
 | 
						|
            {
 | 
						|
              selector: 'node',
 | 
						|
              style: {
 | 
						|
                label: 'data(label)',
 | 
						|
                'background-color': (node) => groupColorMap[node.data('group')] || '#ccc',
 | 
						|
                'text-valign': 'center',
 | 
						|
                'text-halign': 'center',
 | 
						|
                'width': 30,
 | 
						|
                'height': 30
 | 
						|
              }
 | 
						|
            },
 | 
						|
            {
 | 
						|
              selector: 'edge',
 | 
						|
              style: {
 | 
						|
                'line-color': '#ccc',
 | 
						|
                'target-arrow-shape': 'triangle'
 | 
						|
              }
 | 
						|
            }
 | 
						|
          ],
 | 
						|
          layout: { name: 'preset' },
 | 
						|
          zoomingEnabled: true,
 | 
						|
          userZoomingEnabled: true
 | 
						|
        });
 | 
						|
        
 | 
						|
        setTimeout(() => {
 | 
						|
          if (typeof applyCustomZoomHandling === 'function') {
 | 
						|
            applyCustomZoomHandling(cy.value);
 | 
						|
          }
 | 
						|
        }, 100);
 | 
						|
      });
 | 
						|
  });
 | 
						|
  
 | 
						|
  function focusGroup() {
 | 
						|
    if (!cy.value || !selectedGroup.value) return;
 | 
						|
  
 | 
						|
    const groupNodes = cy.value.nodes().filter(ele => ele.data('group') === selectedGroup.value);
 | 
						|
    if (groupNodes.length === 0) return;
 | 
						|
  
 | 
						|
    const bb = groupNodes.boundingBox();
 | 
						|
    const container = cy.value.container();
 | 
						|
    const viewportWidth = container.clientWidth;
 | 
						|
    const viewportHeight = container.clientHeight;
 | 
						|
    const padding = 40;
 | 
						|
    const zoomX = (viewportWidth - 2 * padding) / bb.w;
 | 
						|
    const zoomY = (viewportHeight - 2 * padding) / bb.h;
 | 
						|
    const zoom = Math.min(zoomX, zoomY, 5);
 | 
						|
  
 | 
						|
    cy.value.animate({
 | 
						|
        zoom: zoom,
 | 
						|
        center: { eles: groupNodes }
 | 
						|
    }, {
 | 
						|
        duration: 800
 | 
						|
    });
 | 
						|
  }
 | 
						|
  </script>
 | 
						|
  
 | 
						|
  <style>
 | 
						|
  .cy-container {
 | 
						|
    width: 100vw !important;
 | 
						|
    max-width: 100vw !important;
 | 
						|
    min-width: 0 !important;
 | 
						|
    height: 800px !important;
 | 
						|
    overflow: hidden !important;
 | 
						|
    position: relative;
 | 
						|
  }
 | 
						|
  </style>
 | 
						|
   |