packages/showcase/sunbursts/basic-sunburst.js (96 lines of code) (raw):

// Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import React from 'react'; import Sunburst from 'react-vis/sunburst'; import {EXTENDED_DISCRETE_COLOR_RANGE} from 'react-vis/theme'; import {LabelSeries} from 'react-vis'; import D3FlareData from '../datasets/d3-flare-example.json'; const LABEL_STYLE = { fontSize: '8px', textAnchor: 'middle' }; /** * Recursively work backwards from highlighted node to find path of valud nodes * @param {Object} node - the current node being considered * @returns {Array} an array of strings describing the key route to the current node */ function getKeyPath(node) { if (!node.parent) { return ['root']; } return [(node.data && node.data.name) || node.name].concat( getKeyPath(node.parent) ); } /** * Recursively modify data depending on whether or not each cell has been selected by the hover/highlight * @param {Object} data - the current node being considered * @param {Object|Boolean} keyPath - a map of keys that are in the highlight path * if this is false then all nodes are marked as selected * @returns {Object} Updated tree structure */ function updateData(data, keyPath) { if (data.children) { data.children.map(child => updateData(child, keyPath)); } // add a fill to all the uncolored cells if (!data.hex) { data.style = { fill: EXTENDED_DISCRETE_COLOR_RANGE[5] }; } data.style = { ...data.style, fillOpacity: keyPath && !keyPath[data.name] ? 0.2 : 1 }; return data; } const decoratedData = updateData(D3FlareData, false); export default class BasicSunburst extends React.Component { state = { pathValue: false, data: decoratedData, finalValue: 'SUNBURST', clicked: false }; render() { const {clicked, data, finalValue, pathValue} = this.state; return ( <div className="basic-sunburst-example-wrapper"> <div> {clicked ? 'click to unlock selection' : 'click to lock selection'} </div> <Sunburst animation className="basic-sunburst-example" hideRootNode onValueMouseOver={node => { if (clicked) { return; } const path = getKeyPath(node).reverse(); const pathAsMap = path.reduce((res, row) => { res[row] = true; return res; }, {}); this.setState({ finalValue: path[path.length - 1], pathValue: path.join(' > '), data: updateData(decoratedData, pathAsMap) }); }} onValueMouseOut={() => clicked ? () => {} : this.setState({ pathValue: false, finalValue: false, data: updateData(decoratedData, false) }) } onValueClick={() => this.setState({clicked: !clicked})} style={{ stroke: '#ddd', strokeOpacity: 0.3, strokeWidth: '0.5' }} colorType="literal" getSize={d => d.value} getColor={d => d.hex} data={data} height={300} width={350} > {finalValue && ( <LabelSeries data={[{x: 0, y: 0, label: finalValue, style: LABEL_STYLE}]} /> )} </Sunburst> <div className="basic-sunburst-example-path-name">{pathValue}</div> </div> ); } }