vasturiano / sunburst-chart

A sunburst interactive chart web component for visualizing hierarchical data
https://vasturiano.github.io/sunburst-chart/example/flare/
MIT License
288 stars 89 forks source link

How to use focusOnNode #2

Open hasyee opened 6 years ago

hasyee commented 6 years ago

How can I use focusOnNode method. From the README it's not clear. Can I catch the click event on nodes?

vasturiano commented 6 years ago

@jayhasyee assuming the data looks something like:

const myData = {
  name: "root"
  children: [
    {
      name: "leafA",
      value: 3
    },
    {
      name: "nodeB",
      children: [
        {
          name: "leafBA",
          value: 5
        },
        {
          name: "leafBB",
          value: 1
        }
      ]
    }
  ]
}

you can pass one of the (nested) data nodes as function argument, i.e.:

myChart.focusOnNode(myData.children[1].children[0])

This is only necessary if you wish to zoom programatically on a node triggered by a custom event. The click-to-zoom functionality is already included by default, and call the above method internally.

hasyee commented 6 years ago

Thanks! Is there any way to listen the node click event? As I see, not. I use this now:

document.querySelectorAll('g.slice').forEach(g => {
  g.addEventListener('click', () => {
    const node = myChart.focusOnNode();
    // do something
  });
});

But it would be useful if it had. Something like this:

Sunburst()
  .data(data)
  .onNodeClick(node => { ... })
  ...

or just an .on method with two arguments: .on('nodeClick', node => {...}), which is can be used for other events later.

vasturiano commented 6 years ago

@jayhasyee good suggestion. I added a new event handler: onNodeClick(node) to allow the scenario you described. If that method is overriden, the chart will no longer automatically focus on clicked nodes, so you'd have to do something like the following to maintain that behavior:

myChart
  .onNodeClick(node => {
    myChart.focusOnNode(node);

    // do other operations with node
  })

Please try with the latest release (1.2.0) and let me know if this works for you.

hasyee commented 6 years ago

First, thanks for your improve :) I think focusOnNode and onNodeClick should work independently from each other. https://github.com/vasturiano/sunburst-chart/blob/master/src/sunburst.js#L161 So instead of this:

.on('click', d => {
  d3Event.stopPropagation();
  (state.onNodeClick || this.focusOnNode)(d);
})

something like this:

.on('click', d => {
  d3Event.stopPropagation();
  this.focusOnNode(d);
  state.onNodeClick(d);
})

Or

instead of onNodeClick event, an onNodeSelect event should be more generally. The onNodeSelect is fired even a user click a slice or even a developer call the focusOnNode programatically. In this case onNodeSelect should be fired within focusOnNode after this line: https://github.com/vasturiano/sunburst-chart/blob/master/src/sunburst.js#L36

What do you think?

vasturiano commented 6 years ago

@jayhasyee thanks. The reasoning for exposing (and overriding) the click behavior is that it's the only way (at least without further complicating the module's config) to allow the consumer to cancel the "click-to-focus" functionality, if wanted, since that is just a convenience automatic binding which can be reproduced externally.

As for triggering onNodeSelect whenever focusOnNode is called programmatically, that could lead to infinite event loops depending on the consumer's implementation, so I'd rather avoid it. In any case, it can be detected and fully controlled externally with the current api.

Basically, the choice was so to keep the api as simple as possible without imposing any functionality limitations on the consumer.

Thanks again for bringing it up though!

cjkirk09 commented 5 years ago

I am having trouble using the focusOnNode method to programatically update the sunburst chart.

My data looks like:

this.chart_data = {  // reduced in size for example's sake
  "name": "node1",
  "is_node": true,
  "uuid": "315d139cecb342ba80a4737a2e0d81c0",
  "color": "#2196F3",
  "size": 4,
  "children": [
    {
      "name": "node2",
      "is_node": true,
      "uuid": "b5a03196bc3c4070b775bc59452d63bd",
      "color": "#2196F3",
      "size": 4,
      "children": [
        {
          "name": "leaf1",
          "is_leaf": true,
          "uuid": "26bbb42cb1b9482b9a0d3bb6770f078e",
          "color": "#1EBE78",
          "size": 2
        },
        {
          "name": "leaf2",
          "is_leaf": true,
          "uuid": "e4bf91d78ae041e7b821f10479897d24",
          "color": "#1EBE78",
          "size": 2
        },
        {
          "name": "leaf3",
          "is_leaf": true,
          "uuid": "7e922d5bf781460aa6cf7026fe99863c",
          "color": "#1EBE78",
          "size": 2
        },
        {
          "name": "leaf4",
          "is_leaf": true,
          "uuid": "c1e491ffc88a40cab8d294186ded4daa",
          "color": "#1EBE78",
          "size": 2
        }
      ]
    },
    {
      "name": "leaf5",
      "is_leaf": true,
      "uuid": "0fbe5c99aad2433187ee518f3741b7e1",
      "color": "#1EBE78",
      "size": 2
    },
    {
      "name": "leaf6",
      "is_leaf": true,
      "uuid": "aa0ca66d3f4e478c9a46f6e7a0577481",
      "color": "#1EBE78",
      "size": 2
    }
  ]
}

and I am using the following to programmatically find a node (from an outside selection) and select it in the sunburst chart

findChartNode(chart_node, node_uuid) {
    if (chart_node.uuid === node_uuid) {
        return chart_node;
    }
    if (chart_node.children) {
        for (let i = 0; i < chart_node.children.length; i++) {  // depth first search
            let child_ret = this.findChartNode(chart_node.children[i], node_uuid);
            if (child_ret) {
                return child_ret;
            }
        }
    }
    return null;
}
this.hierarchySunburstChart.focusOnNode(this.findChartNode(this.chart_data, this.selected_node_id))

When I print out what the findChartNode function returns, it finds what it is supposed to, but the call to focusOnNode causes the sunburst chart to vanish.

I'm not sure what is going on, because that looks like what you said to do up above.

I am using version 1.2.2 with angular 5 on Chrome.

vasturiano commented 5 years ago

@cjkirk09 the focusOnNode method was expecting an internal hierarchical node instead of an external data node, which was incorrect. I've fixed this. Please try your example with v1.3.0, it should be working. Let me know if you run into any issues.

cjkirk09 commented 5 years ago

@vasturiano That did the trick. Thanks for the speedy response!

JP-Basson commented 5 years ago

@vasturiano with a similar structure to cj, how would I be able to focus on root node automatically after creation binding it to a variable to display information.

I am programatically doing stuff when a new node is in focus and need it to focus on startup.

For instance when I click on a child / slice I save that node to a variable and I'll display further information about it, would just like to do that on sunburst creation for root node

// this is how im handling each event after initial load

this.sunburstChart
      .onNodeClick((node) => {
        this.sunburstChart.focusOnNode(node);
        // do other operations with node
        this.clickedNode = node;
      });
vasturiano commented 5 years ago

@JP-Basson to reset the zoom to the root node, just pass null to focusOnNode:

sunburstChart.focusOnNode(null)
JP-Basson commented 5 years ago

@vasturiano Think I can better explain my question now.

Is there anyway on load to get .onNodeClick((node))

That node information being generated by on click, is there anyway to get that information pre click? on load of the sunburst? As I'd like to populate the root node info on the side screen when it's generated

vasturiano commented 5 years ago

@JP-Basson I'm not sure I understand the intent. By 'on load' do you mean initialization? Also, the root node info should be available when clicking on the root node itself.

Could you please describe a bit more in detail the issue you're having?

JP-Basson commented 5 years ago

@vasturiano Yeah, on initialisation I would like to get the root node info without clicking on it. Is that possible?

vasturiano commented 5 years ago

@JP-Basson if you don't need it based on user interaction, you should be able to read that info directly from the data object that you pass into the chart. You can even request it from the chart itself:

const rootData = myChart.data();
linuradu commented 4 years ago

@cjkirk09 How did you managed to integrate the sunburt-chart with Angular 5?

linuradu commented 4 years ago

@cjkirk09 How did you managed to integrate the sunburt-chart with Angular 5?

Here is a working solution with Angular 9 https://stackoverflow.com/questions/60541172/integrate-vasturiano-sunbrust-chart-with-angular-2-9/60544969#60544969

Abhiram-vv commented 3 years ago

is it possible to provide a start and end angle for the sunburst-graph? I would like to make it semi-circle!