From 3170bbf1e24dd27893c02196452a69c338678091 Mon Sep 17 00:00:00 2001 From: Will Friebel Date: Tue, 4 Jun 2019 15:31:44 -0700 Subject: [PATCH] Add horizontal layout engine --- .../layout-engine-config.test.js | 12 ++-- .../layout-engine/horizontal-tree.js | 66 +++++++++++++++++++ .../layout-engine/layout-engine-config.js | 13 ++-- .../layout-engine/layout-engine-types.js | 6 +- 4 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 src/utilities/layout-engine/horizontal-tree.js diff --git a/__tests__/utilities/layout-engine/layout-engine-config.test.js b/__tests__/utilities/layout-engine/layout-engine-config.test.js index 93114a23..9a65dfc1 100644 --- a/__tests__/utilities/layout-engine/layout-engine-config.test.js +++ b/__tests__/utilities/layout-engine/layout-engine-config.test.js @@ -1,18 +1,18 @@ - // @flow -import * as React from 'react'; +import * as React from "react"; -import { LayoutEngines } from '../../../src/utilities/layout-engine/layout-engine-config'; +import { LayoutEngines } from "../../../src/utilities/layout-engine/layout-engine-config"; -describe('LayoutEngineConfig', () => { +describe("LayoutEngineConfig", () => { let output = null; - describe('class', () => { - it('is defined', () => { + describe("class", () => { + it("is defined", () => { expect(LayoutEngines).toBeDefined(); expect(LayoutEngines.None).toBeDefined(); expect(LayoutEngines.SnapToGrid).toBeDefined(); expect(LayoutEngines.VerticalTree).toBeDefined(); + expect(LayoutEngines.HorizontalTree).toBeDefined(); }); }); }); diff --git a/src/utilities/layout-engine/horizontal-tree.js b/src/utilities/layout-engine/horizontal-tree.js new file mode 100644 index 00000000..cf0ecc8b --- /dev/null +++ b/src/utilities/layout-engine/horizontal-tree.js @@ -0,0 +1,66 @@ +// @flow +/* + Copyright(c) 2018 Uber Technologies, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import * as dagre from "dagre"; +import { type INode } from "../../components/node"; +import SnapToGrid from "./snap-to-grid"; + +class HorizontalTree extends SnapToGrid { + adjustNodes(nodes: INode[], nodesMap?: any): INode[] { + const { nodeKey, nodeSize } = this.graphViewProps; + const size = (nodeSize || 1) * 1.5; + const g = new dagre.graphlib.Graph(); + g.setGraph({ rankdir: "LR" }); + g.setDefaultEdgeLabel(() => ({})); + + nodes.forEach(node => { + if (!nodesMap) { + return; + } + const nodeId = node[nodeKey]; + const nodeKeyId = `key-${nodeId}`; + const nodesMapNode = nodesMap[nodeKeyId]; + + // prevent disconnected nodes from being part of the graph + if ( + nodesMapNode.incomingEdges.length === 0 && + nodesMapNode.outgoingEdges.length === 0 + ) { + return; + } + g.setNode(nodeKeyId, { width: size, height: size }); + nodesMapNode.outgoingEdges.forEach(edge => { + g.setEdge(nodeKeyId, `key-${edge.target}`); + }); + }); + + dagre.layout(g); + + g.nodes().forEach(gNodeId => { + const nodesMapNode = nodesMap[gNodeId]; + + // gNode is the dagre representation + const gNode = g.node(gNodeId); + + nodesMapNode.node.x = gNode.x; + nodesMapNode.node.y = gNode.y; + }); + return nodes; + } +} + +export default HorizontalTree; diff --git a/src/utilities/layout-engine/layout-engine-config.js b/src/utilities/layout-engine/layout-engine-config.js index eb0afa44..2c544fc5 100644 --- a/src/utilities/layout-engine/layout-engine-config.js +++ b/src/utilities/layout-engine/layout-engine-config.js @@ -15,15 +15,16 @@ limitations under the License. */ -import None from './none'; -import SnapToGrid from './snap-to-grid'; -import VerticalTree from './vertical-tree'; +import None from "./none"; +import SnapToGrid from "./snap-to-grid"; +import VerticalTree from "./vertical-tree"; +import HorizontalTree from "./horizontal-tree"; - -export type LayoutEngine = None | SnapToGrid | VerticalTree; +export type LayoutEngine = None | SnapToGrid | VerticalTree | HorizontalTree; export const LayoutEngines = { None, SnapToGrid, - VerticalTree + VerticalTree, + HorizontalTree }; diff --git a/src/utilities/layout-engine/layout-engine-types.js b/src/utilities/layout-engine/layout-engine-types.js index 3eedbf77..f9defe8c 100644 --- a/src/utilities/layout-engine/layout-engine-types.js +++ b/src/utilities/layout-engine/layout-engine-types.js @@ -1,3 +1,7 @@ // @flow -export type LayoutEngineType = 'None' | 'SnapToGrid' | 'VerticalTree'; +export type LayoutEngineType = + | "None" + | "SnapToGrid" + | "VerticalTree" + | "HorizontalTree";