Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collapsible tree example #162

Closed
techniq opened this issue Oct 3, 2017 · 28 comments
Closed

Collapsible tree example #162

techniq opened this issue Oct 3, 2017 · 28 comments

Comments

@techniq
Copy link
Collaborator

techniq commented Oct 3, 2017

I derived a simple example from https://vx-demo.now.sh/trees available here: https://codesandbox.io/s/n3w687vmqj

I'm not overly happy with the forceUpdate() and would like to have it animate similar to https://bl.ocks.org/mbostock/4339083, but it's a start

@techniq
Copy link
Collaborator Author

techniq commented Oct 5, 2017

@sghall - any tips on how to use react-move to produce something similar to https://bl.ocks.org/mbostock/4339083?

@techniq
Copy link
Collaborator Author

techniq commented Oct 5, 2017

@hshoff / @sghall - It seems like to be able to make this work we would need to be able to add a <NodeGroup /> here

Maybe if <Tree/> / <Cluster/> receives a children function, we call it instead? Such as:

<Tree
  top={margin.top}
  left={margin.left}
  root={hierarchy(data, d => d.isExpanded ? d.children : null)}
  size={[
    height - margin.top - margin.bottom,
    width - margin.left - margin.right
  ]}
>
  ({ links, descendants }) => (
    // magic here
  )
</Tree>

@hshoff
Copy link
Member

hshoff commented Oct 5, 2017

@techniq yup that’s a great solution. I’ve been busy with interactions, will get to this when i can. Happy to a review a PR if anyone wants to take a swing at this.

@techniq
Copy link
Collaborator Author

techniq commented Oct 5, 2017

@hshoff No promises, but I'll see if I can get a PR pushed up soonish (should be fairly trivial) but currently this is me just kicking around vx so my other tasks have higher priority.

Seems we would want the same with vx-network and maybe other layouts. I've only recently been getting into d3 beyond the basics so I'm not aware of all the different layouts and their intricacies and how vx has integrated them.

I would love to hear if @sghall has any feedback on this with regards to react-move.

Lastly, I wonder if the child function / render prop should be embraced whole-heartedly throughout vx, in place of some HOCs (such as replacing withTooltip, withParentSize, withScreenSize, ...). I've had good success with it in my react-fetch-component and downshift, and I know Michael Jackson has been a big proponent of it lately

@techniq
Copy link
Collaborator Author

techniq commented Oct 5, 2017

Also, just leaving this for later reference, but it would be nice if the <Tree /> example could, along with being collapsible, show how to reveal a node, similar to Search Collapsible Tree. This drag and drop and pannable example would be nice as well (maybe after from of your current vx-drag / vx-zoom work)

@sghall
Copy link

sghall commented Oct 5, 2017

Hey. Off the top of my head, I'd say it's two NodeGroups for nodes and links. Do the links first so the nodes are on top in the SVG...something like...

<Tree
  top={margin.top}
  left={margin.left}
  root={hierarchy(data, d => d.isExpanded ? d.children : null)}
  size={[
    height - margin.top - margin.bottom,
    width - margin.left - margin.right
  ]}
>
  ({ links, descendants }) => (
    <NodeGroup
      data={links} 
      ...
    </NodeGroup>
    <NodeGroup
      data={descendants} 
      ...
    </NodeGroup>
  )
</Tree>

He's doing some magic here with stashing the last layout and using the "diagonal" path generator to get those nice curved lines. You can pass a custom interpolator to react-move to handle that.

This flubber example is using a custom interpolator...
https://react-move.js.org/#/documentation/animate

Also, this piece storing if the children are collapsed is a little sticky. Need some state somewhere with that.

  function collapse(d) {
    if (d.children) {
      d._children = d.children;
      d._children.forEach(collapse);
      d.children = null;
    }
  }

  root.children.forEach(collapse);
  update(root);

If you get something committed I can jump in there and hack on it a little if there's issues.

@techniq
Copy link
Collaborator Author

techniq commented Oct 5, 2017

Thanks @sghall. I have the expand/collapse working currently in this codesandbox and was looking to moving to the next step of animating.

Once I or @hshoff get the child function in place I'll take a stab at the transition but maybe ping you with a WIP codesandbox if I get stuck (especially the the interpolation)?

Looking at the d3 example (linked in the first post), it looks like Mike is using...

var diagonal = d3.svg.diagonal()
    .projection(function(d) { return [d.y, d.x]; });

...for the interpolation so we might be able to do something similar...

@techniq
Copy link
Collaborator Author

techniq commented Oct 5, 2017

Actually d3.svg.diagonal() was replaced in d3 v4 with linkVertical (or horizontal/radial) and is what vx's LinkVertical uses

@techniq
Copy link
Collaborator Author

techniq commented Oct 6, 2017

@hshoff @sghall I've made some progress using react-move to animate the expand/collapse animations of a <Tree /> after the render prop was exposed (thanks again @hshoff )

vx_tree_react_move

There is still some work to do:

  • Animate the links (source, target)
  • Investigate using d3.linkVertical / d3.linkHorizontal as a custom interpolator for nodes so they slide along the link paths instead of a straight line
  • Cleanup the example and probably refine the approach (not using this.forceUpdate(), etc)

I currently have it running locally but can move it out to codesandbox.io once a new version of vx-hierarchy is released (I couldn't figure out how to use a git sha version on codesandbox, and also struggled locally with the same, but ended up getting npm link to work and have been tweaking the vx-demo for Tree).

I don't know if I'll get much more time to iterate on this till next week so once I get it on codesandbox, feel free to fork and tweak if you want.

@hshoff
Copy link
Member

hshoff commented Oct 6, 2017

awesome!

@hshoff
Copy link
Member

hshoff commented Oct 6, 2017

just published v0.0.141 changelog

@techniq
Copy link
Collaborator Author

techniq commented Oct 8, 2017

Some progress animating links and nodes now collapse to closing parent/grandparent/etc (not just the parent). I've updated my codesandbox with these changes if you want to take a look at the code.

vx_tree_react_move_20171008

A few items still needing addressed

Expanding grandchildren from grandparent

While collapsing a grandparent will fold all children/grandchildren/etc into the node, expanding currently only transitions from each children's parent's location. This is best demonstrated with an example. Notice when expanding T, nodes A1, A2, A3, C, and others (and their links) do not appear to be sourced from T.

vx_tree_react_move_20171008_expanding_grandparent_issue

Order of collapsing

Based on how I'm saving x0/y0 only when a node is expanding, if a sibling node is expanded and then the original node is collapsed, the transitions are incorrect. For example,

  • Expand T
  • Expand A
  • Expand B
  • Collapse A (wrong transition)
  • Collapse B (wrong transition)

vx_tree_react_move_20171008_collasing_order_issue

There may be other issues as well, especially when compared to the canonical d3 example, but it's progressing. If anyone has any suggestions (alternative approaches) to resolve these issues I'm interested.

@techniq
Copy link
Collaborator Author

techniq commented Oct 9, 2017

I created a radial tree example derived from the cartesian layout version and based on the Radial Tidy Tree d3 example

vx_tree_react_move_20171009_radial_example

The only changes needed to support this layout was to:

  • Translate the main group within Tree to the center (i.e <Group top={width / 2} left={height / 2}>)
  • Use LinkRadial from @vx/shape instead of LinkHorizontal for links
  • Use pointRadial from d3-shape to translate the each node's x/y to the radial layout. I also used a simple function to return an {x, y} object instead of an array of [x, y]

This example also has the same quirks/issues as mentioned in previous comment, but so far I'm really enjoying using vx and react-move together.

@hshoff
Copy link
Member

hshoff commented Oct 9, 2017

@techniq nice! i added a link to your example to the in the wild section of the readme here: ee2da22

(looks like your radial example link goes to the same place as the cartesian example, happy to add that as well once you have an updated link)

@techniq
Copy link
Collaborator Author

techniq commented Oct 9, 2017

Oops, link fixed above (and thanks)

Btw, if you or @sghall (probably more likely) have any ideas on the transition quirks, I'm all ears as I'm a bit stumped at the moment.

@Vaga91
Copy link

Vaga91 commented Feb 13, 2018

Hello dears, I use this tree to display data, and I want to thank you for this work.
There is one problem, please help.
With large data When the layout is polar and the link is a curve nodes overlap each other (you can see here https://codesandbox.io/s/p5pkn9jwx on Expand A ).
It is possible to do something to fix this problem?
Thanks.

@techniq
Copy link
Collaborator Author

techniq commented Feb 14, 2018

@Vaga91 you could adjust the separation prop. The larger you make the ratio, the greater the spread between sibling nodes.

Your example

separation={(a, b) => (a.parent == b.parent ? 1 : .5) / a.depth}
image

Possible solution

separation={(a, b) => (a.parent == b.parent ? 1 : 5) / a.depth}
image

Feel free to experiment with other values.

You might also want to look into enlarging your rendered size and allowing pan/zoom. We have a component coming soon, but you might want to look at this PanZoom component in the short term.

@hshoff
Copy link
Member

hshoff commented Feb 14, 2018

Closing this as examples were added in #234. Can view them here: https://vx-demo.now.sh/linkTypes

@Vaga91 feel free to continue the discussion here.

@hshoff hshoff closed this as completed Feb 14, 2018
@Vaga91
Copy link

Vaga91 commented Feb 21, 2018

@techniq I've been experimenting these few days. And here is the result Tree
I added separation={(a, b) => ((a.children === b.children) && (a.parent === b.parent) ? 1 : 7) / a.depth }
It works, but needs to be improved.

Thank you :)

@UltimateForm
Copy link

UltimateForm commented Jul 26, 2019

hey @techniq do you remember this example? i've been working on top of what you had done, updating it (packages and to typescript(which in hindsight might have not been the best idea) )trying to expand it a little bit to fit some of my needs (node selection, adding removal, filtering, etc, click around to see) - https://codesandbox.io/s/vx-collapsible-tree-typescript-oxewc
but i have a some concerns, mostly related to performance and possible over-complexity, see I need it to be "responsive" since the tree will be representing a tree data structure built and maintained by a backoffice user, right now i'm using react-measure for responsive Tree Canvas size and an injected ref into the Text element with getBoundingClientRect() for responsive node size -these things also require rerendering for the correction of positions, i know of #375 and also of https://vx-demo.now.sh/responsive (but this one seems quite similar to react-measure), i'm bothering you with this because i thought that maybe you developed it more since last time and could give me some tips

@techniq
Copy link
Collaborator Author

techniq commented Jul 26, 2019

Hey @UltimateForm, I do remember that example, but it's been a while since I've looked at it. I like the features you've added to it, especially the node selection with path. Sadly, I haven't worked on this in some time, and my usage was mostly for a one off for a presentation. With that said, I do have plans to use this in an upcoming feature in one of our applications, but it's not currently on the roadmap (probably a few months out at least). If I make any improvements (I plan to push them down to vx if they make sense) I will try to remember to ping you in this thread.

With that said, the demo seems to interact well, but I'm assuming you have many more nodes than I'm creating using the tool? I know my tree was made up of ~3000 nodes (personnel structure of a decent size company) and it seems to function well (but I would never expand more than a handful of nodes at a time). I mostly ran into canvas size constraints as well, and might have opened more if I used @vx/zoom that it wasn't available at the time.

Let me know if you make any more progress on your side, and I'll be sure to do the same on mine.

@UltimateForm
Copy link

UltimateForm commented Jul 26, 2019

Thanks for the quick reply @techniq 🙏 I didn't quite understand what you meant by

I'm assuming you have many more nodes than I'm creating using the tool

on the condesandbox i sent the nodes are only created on user input, if you're talking about the growth potential, I have not tested the tool with that many nodes, mainly because the usecase im doing this for does not benefit from that many nodes, i'd even say it suffers, but i should, for test purposes at least use a pre-built data structure with tons of nodes, i'm sure the current performance issues would be more noticeable that way

besides vx/responsive i'm also considering using vx/zoom and vx/drag to add more maneuverability to the tree

Either way, i'm really happy to know you're planning on revisiting this, I'll keep working on my side as well. best of luck 🙏

edit: https://github.com/UltimateForm/vx-collapsible-tree-tsx the repo, in case you feel like checking any future work on it

@mindceb
Copy link

mindceb commented Mar 22, 2020

@techniq Hi. I using you sample, but i need help with collapse all tree's elements at init. How can i do this, with visible links?

@techniq
Copy link
Collaborator Author

techniq commented Mar 22, 2020

@mindceb I'm having trouble following what you're asking, do you mean to only show the root node at the first (although this is what my example does so I don't think it's that.

Can you create a CodeSandbox with your current progress?

@alexschachne
Copy link

alexschachne commented Jun 13, 2020

@techniq Any chance you've gotten the entire tree to animate out on page load? I know you can expand onClick but am looking to have it start expanded out and be collapsable. Let me know, thanks!

@techniq
Copy link
Collaborator Author

techniq commented Jun 13, 2020

@alexschachne I do not. Currently that example stores the isExpanded as a prop on each Node, but I think it might be best to track it in a separate state array.

instead of...

const root = hierarchy(data, d => (d.isExpanded ? d.children : null))

something like...

const [expandedIds, setExpandedIds = useState([]);
const root = hierarchy(data, d => expandedIds.includes(d.id) ? d.children : null))

and then use an useEffect() to expand all the items (might also be able to use root.each:

useEffect(() => {
  setExpandedIds(d.map(x => x.id))
}, []);

and update the onClick on the node rect to add/remove that node's id from the array (would remove the forceUpdate as well).

Let me know if this works.


Side note, I've been experimenting with various hierarchy layouts and vx (including animations and such). Each layout is in a various state of experimentation (and none are by any means "done".
I also haven't added all the layouts I plan to, include Tree, Circle Pack, Chord, etc).
https://github.com/techniq/vx-hierarchy-examples
https://nxmen.csb.app/

@techniq
Copy link
Collaborator Author

techniq commented Jun 13, 2020

@alexschachne Actually it may be harder than this...

I added a quick Tree example (based on that old codesandbox and some some changes I made within my projects) and updated to store expandedNodeKeys instead of adding isExpanded to the node itself... this worked good, but attempting to expand all the nodes at startup causes a react-spring error:

image

I also tried not filtering any of the children so the full tree would be rendered, but it too threw the error.

Probably since it tween's from the parent/last position.

@techniq
Copy link
Collaborator Author

techniq commented Jun 13, 2020

@alexschachne The problem was due to the Links (if you commented them out, the Nodes animated correctly). I think I have this fixed now, but the example tree I'm using is rather heavy. Need to test with a smaller tree (I have issues with react-spring and the tree when running in dev mode (seems to do better when using a prod build).

Anyways, it's not perfect, but might get you closer (and the pattern to track the ids in a separate array is a better pattern.. using a Set and Set.has() would be more performance than Array.includes and doing scans, but the number of expanded items should be pretty small.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants