github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/cmd/noms/splore/src/layout.js (about) 1 // Copyright 2017 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 // @flow 6 7 import React, {Element} from 'react'; 8 import {TreeNode} from './buchheim.js'; 9 import Node from './node.js'; 10 import type {NodeGraph} from './types.js'; 11 12 type Props = { 13 data: NodeGraph, 14 onNodeClick: (e: MouseEvent, s: string) => any, 15 tree: TreeNode, 16 }; 17 18 export default function Layout(props: Props): Element<any> { 19 const {data, onNodeClick, tree} = props; 20 21 const layoutChildren = []; 22 const edges = []; 23 const keyEdges = []; 24 const lookup = {}; 25 26 const spaceX = 100; 27 const spaceY = 20; 28 const paddingRight = 250; 29 const getX = d => d.y * spaceX; 30 const getY = d => d.x * spaceY; 31 let maxX = 0; 32 let minY = 0; 33 let maxY = 0; 34 35 const process = (treeNode: TreeNode, fromX: number, fromY: number) => { 36 const {children, id, isOpen, node} = treeNode; 37 const x = getX(treeNode); 38 const y = getY(treeNode); 39 40 maxX = Math.max(x + spaceX, maxX); 41 minY = Math.min(y, minY); 42 maxY = Math.max(y + spaceY, maxY); 43 44 const n = ( 45 <Node 46 fromX={fromX} 47 fromY={fromY} 48 hasChildren={node.hasChildren} 49 isOpen={isOpen} 50 key={'node$' + id} 51 onClick={e => onNodeClick(e, id)} 52 spaceX={spaceX} 53 text={node.name} 54 x={x} 55 y={y} 56 /> 57 ); 58 59 layoutChildren.push(n); 60 lookup[id] = treeNode; 61 62 children.forEach(c => { 63 process(c, x, y); 64 }); 65 66 // Only show links if the tree is open. 67 if (isOpen) { 68 data.links[id].forEach(lid => { 69 edges.push([id, lid]); 70 }); 71 } 72 73 // Always show key links (key -> value and label -> value) even if the tree 74 // is closed. 75 data.keyLinks[id].forEach(lid => { 76 keyEdges.push([id, lid]); 77 }); 78 }; 79 80 process(tree, 0, 0, true); 81 82 const edgeStyle = { 83 stroke: '#ccc', 84 strokeWidth: '1.5px', 85 }; 86 87 edges.forEach(e => { 88 const from = lookup[e[0]]; 89 const to = lookup[e[1]]; 90 layoutChildren.push( 91 <path 92 key={'edge$' + e[0] + '-' + e[1]} 93 style={edgeStyle} 94 d={`M${getX(from)},${getY(from)}L${getX(to)},${getY(to)}`} 95 />, 96 ); 97 }); 98 99 const keyEdgeStyle = { 100 ...edgeStyle, 101 stroke: 'steelblue', 102 }; 103 104 keyEdges.forEach(e => { 105 const from = lookup[e[0]]; 106 const to = lookup[e[1]]; 107 layoutChildren.push( 108 <path 109 key={'keyEdge$' + e[0] + '-' + e[1]} 110 style={keyEdgeStyle} 111 d={`M${getX(from)},${getY(from)}L${getX(to)},${getY(to)}`} 112 />, 113 ); 114 }); 115 116 const sortOrder = elm => (elm.type === 'path' ? 0 : 1); 117 layoutChildren.sort((a, b) => sortOrder(a) - sortOrder(b)); 118 119 let translateY = spaceY; 120 if (minY < 0) { 121 translateY -= minY; 122 maxY -= minY; 123 } 124 125 return ( 126 <svg width={maxX + spaceX + paddingRight} height={maxY + spaceY}> 127 <g transform={`translate(${spaceX}, ${translateY})`}> 128 {layoutChildren} 129 </g> 130 </svg> 131 ); 132 }