/* eslint-disable @typescript-eslint/no-use-before-define */ import React, { useState, useEffect } from 'react'; import { SVG, Svg } from '@svgdotjs/svg.js'; import { SVGIcon } from './utils/icons'; import { Entity, ItemType } from '../types'; interface GraphItem { id: string; update: Function; expanded: boolean; parent: string | undefined; svg: Svg; children: GraphItem[]; } interface Graph { container: Svg | null; items: GraphItem[]; update: Function; entityExists: Function; itemPosition(id: string): number; } const ROW_HEIGHT = 38; const getIcon = (type: ItemType): Function => ({ [ItemType.RIEW]: SVGIcon.Code, [ItemType.CHANNEL]: SVGIcon.Activity, [ItemType.STATE]: SVGIcon.Database, [ItemType.ROUTINE]: SVGIcon.CPU, [ItemType.UNRECOGNIZED]: SVGIcon.Octagon, }[type] || SVGIcon.Octagon); function trimText(str: string, n: number): string { if (str.length > n) { return `${str.substr(0, n)}...`; } return str; } function arrayMove(arr: Array, oldIndex: number, newIndex: number) { if (newIndex >= arr.length) { let k = newIndex - arr.length + 1; while (k--) { arr.push(undefined); } } arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]); } function createGraphItem(entity: Entity, parent: string | undefined) { // defining the label const label = (graph.container as Svg) .nested() .id(entity.id as string) .css('cursor', 'pointer'); function addMoreIcon(e: Entity) { if (!label.findOne(`.${e.id}-more-icon`)) { label.svg( SVGIcon.MoreHorizontal( 200 - 30, 7, undefined, undefined, `${entity.id}-more-icon` ) ); } } function decorate() { label.line([0, ROW_HEIGHT - 1, 200, ROW_HEIGHT - 1]).stroke({ width: 1, color: '#444', }); label.svg(getIcon(entity.type)(10, 10, 18)); } function setText() { label .text(entity.name ? trimText(entity.name, 20) : '') .font({ size: '1em', }) .move(33, 10) .css('fill', 'white'); } function addBackground() { const background = label.rect(200, ROW_HEIGHT).attr({ fill: '#2a2a2a' }); label.on('mouseover', () => background.attr({ fill: '#242424' })); label.on('mouseout', () => background.attr({ fill: '#2a2a2a' })); } const graphItem: GraphItem = { id: entity.id, parent, expanded: false, svg: label, update(e: Entity) { createGraphItems(e.children, this.id); }, }; addBackground(); decorate(); setText(); createGraphItems(entity.children, graphItem.id); return graphItem; } function sortAndPosition() { const log: Array = []; graph.items.forEach(item => { if (item.parent) { const itemPosition = graph.itemPosition(item.id); const parentPosition = graph.itemPosition(item.parent); if (parentPosition >= 0) { if (itemPosition < parentPosition) { arrayMove(graph.items, itemPosition, parentPosition); } else if (itemPosition - parentPosition > 1) { log.push(`${item.id} ${itemPosition - parentPosition}`); } } else { console.warn(`Ops. Parent ${item.parent} doesn't exist.`); } } }); console.clear(); console.log(graph.items); log.forEach(v => console.log(v)); } function createGraphItems( es: Entity[] | undefined, parent?: string | undefined ) { if (es && es.length > 0) { es.forEach(item => { const graphItem = graph.entityExists(item); if (!graphItem) { graph.items.push(createGraphItem(item, parent)); sortAndPosition(); } else { graphItem.update(item); } }); } } const graph: Graph = { container: null, items: [], entityExists(entity: Entity): GraphItem | undefined { return this.items.find(registeredItem => registeredItem.id === entity.id); }, itemPosition(id: string) { return this.items.findIndex(i => i.id === id); }, update: createGraphItems, }; export default function Graph({ entities }: { entities: Entity[] }) { const [g, setGraph] = useState(null); useEffect(() => { if (g === null) { graph.container = SVG() .addTo('#graph') .size('100%', '100%'); createGraphItems(entities); setGraph(graph); } else { g.update(entities); } }, [entities, g]); return
; }