visx icon indicating copy to clipboard operation
visx copied to clipboard

is it possible to create a tree with nodes on either side

Open dagda1 opened this issue 6 years ago • 5 comments

I am looking to create something like this:

I need a tree but with the nodes on either side. I've had create success with vxtree but that has one root node.

Is there a way I could spread them out like the diagram below?

tree

dagda1 avatar Nov 02 '18 22:11 dagda1

I believe the technique I’ve seen using straight d3.js is to render 2 trees with each root at the middle/overlapping.

For example: http://bl.ocks.org/jdarling/2503502

You should be able to use 2 <Tree /> components to accomplish the same thing. See https://vx-demo.now.sh/linkTypes for an example to change direction (you should be able to swap the x-scale’s min/max to go right/left.

techniq avatar Nov 03 '18 00:11 techniq

@techniq the 2 trees approach did not work for me because I need zooming and panning which was difficult to keep the trees together so I ended up with 1 tree:

https://codesandbox.io/s/znj3ykqwj3

I ended up doing the calculations for the nodes in the ./src/components/NodesFanout component but this feels wrong, is there a better way:

import * as React from "react";
import NodeGroup from "react-move/NodeGroup";
import { StructureItem } from "../../types";
import { Node } from "../Node";
import { HierarchyPointNode } from "d3-hierarchy";
import { HierarchyLabelProps } from "../HierarchyLabel";
import { findIndex } from "lodash";
import { NodeHeight } from "../Tree";
import { findCollapsedParent } from "../../util/node";

const { Group } = require("@vx/group");

export interface NodesProps {
  nodes: HierarchyPointNode<StructureItem>[];
  clickHandler: any;
  shapeLength: number;
  collapse: boolean;
  Label: React.ComponentType<HierarchyLabelProps>;
  LabelCollapsed: React.ComponentType<HierarchyLabelProps>;
}

const positionNodes = (
  node: HierarchyPointNode<StructureItem>,
  nodes: HierarchyPointNode<StructureItem>[]
) => {
  let left: number;
  let top: number;

  if (!node.parent) {
    left = node.y / 2 - NodeHeight;
    top = node.x - NodeHeight;
  } else if (node.data && node.data.isRight) {
    const index = findIndex(nodes, node);
    const lastLeft = nodes[index - 1];

    top = lastLeft.x;
    left = NodeHeight;
  } else {
    top = node.x;
    left = node.y;
  }

  return {
    top: [top],
    left: [left],
    opacity: [1]
  };
};

export const NodesFanout: React.SFC<NodesProps> = ({
  Label,
  LabelCollapsed,
  nodes,
  shapeLength,
  clickHandler,
  collapse
}) => {
  return (
    <NodeGroup
      data={nodes}
      keyAccessor={(d: HierarchyPointNode<StructureItem>) => d.data.id}
      start={(node: HierarchyPointNode<StructureItem>) => {
        let left: number;
        let top: number;

        if (!node.parent) {
          left = node.y / 2 - NodeHeight;
          top = node.x - NodeHeight;
        } else {
          left = node.parent.y / 2;
          top = node.parent.x;
        }

        return {
          top,
          left,
          opacity: 0
        };
      }}
      enter={node => positionNodes(node, nodes)}
      update={node => positionNodes(node, nodes)}
      leave={node => {
        let left: number;
        let top: number;

        const collapsedParent = findCollapsedParent(
          node.parent
        ) as HierarchyPointNode<StructureItem>;

        if (!collapsedParent.parent) {
          left = collapsedParent.y / 2;
          top = collapsedParent.x - NodeHeight;
        } else {
          left = collapsedParent.parent.y / 2;
          top = collapsedParent.parent.x - NodeHeight;
        }

        return {
          top: [top],
          left: [left],
          opacity: [0]
        };
      }}
    >
      {nodes => (
        <Group>
          {nodes.map(({ key, data: node, state }, index) => {
            return (
              <Group
                top={state.top}
                left={state.left}
                key={key}
                opacity={state.opacity}
                className={`node__${index}`}
              >
                <Node
                  Label={Label}
                  LabelCollapsed={LabelCollapsed}
                  node={node}
                  shapeLength={shapeLength}
                  clickHandler={(e: any) => {
                    clickHandler({ e, node });
                  }}
                  key={key}
                  collapse={collapse}
                />
              </Group>
            );
          })}
        </Group>
      )}
    </NodeGroup>
  );
};

My animation is a bit wrong also.

dagda1 avatar Nov 22 '18 07:11 dagda1

Hmm, I've not done this myself so I can't be of much more help right now.

You might be able get away using a Polar coordinate system and adjust the size and separation props.

  • https://vx-demo.now.sh/linkTypes
  • https://codesandbox.io/s/vmqwrkl395

See: https://github.com/hshoff/vx/issues/162#issuecomment-365493922 for some experimentation with separation

techniq avatar Nov 22 '18 18:11 techniq

@dagda1 @techniq I know this is a old post, but I have a use case requiring a tree with nodes on each side using vx/hierarchy. Has anyone come up with a solution for this? I haven't had any luck finding a good example.

aoloo avatar Jan 05 '21 20:01 aoloo

@aoloo Sorry I haven't had this use case myself thus far.

When I'm looking for new examples / approaches I'll typically look at d3 observables and port to React / visx.

This NCAA Bracket example might give you a start, I just quickly read through it but it does use d3.hierarchy / d3.tree:

  • https://observablehq.com/@mcmcclur/ncaa-brackets

It appears to be well written and has some good notes. I think it's rendering 4 trees (for each quadrant) and blending them together as 1, but I didn't analyze it deeply.

With hierarchies/trees, you need to have a single root node. You can sometimes work around this by creating a "pseudo" root that you hide visually. You could try that approach, but you'd have to create a tree left to right, as opposed to inside out (with the center being at the middle of the nodes, at opposed to at the root).

This layout might be better represented as a graph structure made of nodes and links, but I think searching for "bracket" helps to identify this type of layout.

Sorry I can't be of more help.

techniq avatar Jan 05 '21 21:01 techniq