Skip to content

Commit

Permalink
Make Radar stateless
Browse files Browse the repository at this point in the history
  • Loading branch information
shauns committed Feb 7, 2017
1 parent b8adff3 commit 73b3f99
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 227 deletions.
97 changes: 49 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,55 @@ This is a Radar chart component for displaying multiple sets of data. Hovering i
```js
import Radar from 'react-d3-radar';
<Radar
width={500}
height={500}
padding={70}
domainMax={10}
onSelect={(point) => {
if (point) {
console.log('hovered over a data point');
} else {
console.log('not over anything');
}
}}
data={{
variables: [
{key: 'resilience', label: 'Resilience'},
{key: 'strength', label: 'Strength'},
{key: 'adaptability', label: 'Adaptability'},
{key: 'creativity', label: 'Creativity'},
{key: 'openness', label: 'Open to Change'},
{key: 'confidence', label: 'Confidence'},
],
sets: [
{
key: 'me',
label: 'My Scores',
values: {
resilience: 4,
strength: 6,
adaptability: 7,
creativity: 2,
openness: 8,
confidence: 1,
},
},
{
key: 'everyone',
label: 'Everyone',
values: {
resilience: 10,
strength: 8,
adaptability: 6,
creativity: 4,
openness: 2,
confidence: 0,
},
},
],
}}
/>
width={500}
height={500}
padding={70}
domainMax={10}
highlighted={null}
onHover={(point) => {
if (point) {
console.log('hovered over a data point');
} else {
console.log('not over anything');
}
}}
data={{
variables: [
{key: 'resilience', label: 'Resilience'},
{key: 'strength', label: 'Strength'},
{key: 'adaptability', label: 'Adaptability'},
{key: 'creativity', label: 'Creativity'},
{key: 'openness', label: 'Open to Change'},
{key: 'confidence', label: 'Confidence'},
],
sets: [
{
key: 'me',
label: 'My Scores',
values: {
resilience: 4,
strength: 6,
adaptability: 7,
creativity: 2,
openness: 8,
confidence: 1,
},
},
{
key: 'everyone',
label: 'Everyone',
values: {
resilience: 10,
strength: 8,
adaptability: 6,
creativity: 4,
openness: 2,
confidence: 0,
},
},
],
}}
/>
```
## API

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-d3-radar",
"version": "0.1.3",
"version": "0.2.1",
"description": "React-based Radar chart for D3",
"main": "lib/react-d3-radar.js",
"repository": "https://github.com/shauns/react-d3-radar.git",
Expand Down
231 changes: 54 additions & 177 deletions src/Radar.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// @flow
import React, {Component} from 'react';
import React from 'react';
import {scaleLinear, schemeCategory10} from 'd3-scale';
import {voronoi} from 'd3-voronoi';
import _ from 'lodash';
import {flatMapDeepArray, forEachArray} from './utils';
import type {TickScale, RadarPoint, RadarData} from './types';
import RadarAxis from './RadarAxis';
import RadarCircle from './RadarCircle';
import RadarRings from './RadarRings';
import RadarWrapper from './RadarWrapper';

function radiusScales(
data: RadarData,
Expand Down Expand Up @@ -61,22 +59,8 @@ type Props = {
padding: number,
domainMax: number,
style?: {},
onSelect?: (point: RadarPoint | null) => void,
};

const defaultRadarStyle = {
numRings: 4,
axisColor: '#cdcdcd',
ringColor: '#cdcdcd',
};

type State = {
selected: ?RadarPoint,
scales: {[variableKey: string]: TickScale},
offsetAngles: {[variableKey: string]: number},
allPoints: Array<{setKey: string, points: Array<RadarPoint>}>,
voronoiDiagram: any,
radius: number,
onHover?: (point: RadarPoint | null) => void,
highlighted: ?RadarPoint,
};

function convertData(props) {
Expand Down Expand Up @@ -106,162 +90,55 @@ function convertData(props) {
return {allPoints, scales, offsetAngles, voronoiDiagram, radius};
}

export default class Radar extends Component {
props: Props;

state: State;

hoverMap = null;

constructor(props: Props) {
super(props);
this.state = {selected: null, ...convertData(props)};
}

componentWillReceiveProps(nextProps: Props) {
this.setState({selected: null, ...convertData(nextProps)});
const {onSelect} = nextProps;
if (onSelect) {
onSelect(null);
}
}

componentDidMount() {
if (this.hoverMap) {
this.hoverMap.addEventListener('mousemove', (event: MouseEvent) => {
const {padding, height, width, onSelect} = this.props;
const {radius} = this.state;
const innerHeight = height - padding * 2;
const innerWidth = width - padding * 2;
const diameter = radius * 2;

let {offsetX: clientX, offsetY: clientY} = event;
clientX -= padding;
clientY -= padding;
clientX -= (innerWidth - diameter) / 2;
clientY -= (innerHeight - diameter) / 2;

const {voronoiDiagram} = this.state;
const site = voronoiDiagram.find(clientX, clientY, radius / 2);
if (!site) {
this.setState({selected: null});
if (onSelect) {
onSelect(null);
}
return;
}

const {data} = site;
const {selected: currentSelected} = this.state;
if (!currentSelected || currentSelected.key !== data.key) {
this.setState({selected: data});
if (onSelect) {
onSelect(data);
}
}
});
}
}

render() {
const {data, width, height, padding, domainMax, style} = this.props;
const {allPoints, scales, offsetAngles, selected, radius} = this.state;
const diameter = radius * 2;
const {axisColor, ringColor, numRings} = {...defaultRadarStyle, ...style};

const selectedSetKey = selected ? selected.setKey : null;

const innerHeight = height - padding * 2;
const innerWidth = width - padding * 2;

const backgroundScale = scales[data.variables[0].key];
const ticks = backgroundScale.ticks(numRings).slice(1);
const tickFormat = backgroundScale.tickFormat(numRings);

const colors = {};
forEachArray(allPoints, ({setKey}, idx) => {
colors[setKey] = schemeCategory10[idx];
});

const [selectedPoints, unSelectedPoints] = _.partition(
allPoints,
({setKey}) => setKey === selectedSetKey,
);
export default function Radar(props: Props) {
const {
data,
width,
height,
padding,
domainMax,
style,
onHover,
highlighted,
} = props;
const {allPoints, scales, offsetAngles, radius, voronoiDiagram} = convertData(
props,
);

const highlightedSetKey = highlighted ? highlighted.setKey : null;

const backgroundScale = scales[data.variables[0].key];

const colors = {};
forEachArray(allPoints, ({setKey}, idx) => {
colors[setKey] = schemeCategory10[idx];
});

return (
<svg width={width} height={height}>
<g
transform={`translate(${padding}, ${padding})`}
ref={c => {
this.hoverMap = c;
}}
>
<rect
width={diameter}
height={diameter}
fill={'transparent'}
transform={
`translate(${(innerWidth - diameter) / 2}, ${(innerHeight -
diameter) /
2})`
}
/>
<g transform={`translate(${innerWidth / 2}, ${innerHeight / 2})`}>
<RadarRings
ticks={ticks}
scale={backgroundScale}
color={ringColor}
format={tickFormat}
/>
{data.variables.map(({key, label}) => {
return (
<RadarAxis
key={key}
scale={scales[key]}
offsetAngle={offsetAngles[key]}
label={label}
domainMax={domainMax}
color={axisColor}
/>
);
})}
{unSelectedPoints.map(({setKey, points}) => {
const isSelected = setKey === selectedSetKey;
const selectedVariableKey = isSelected && selected
? selected.variableKey
: null;
return (
<RadarCircle
key={setKey}
points={points}
scales={scales}
offsetAngles={offsetAngles}
color={colors[setKey]}
isSelected={isSelected}
selectedVariableKey={selectedVariableKey}
/>
);
})}
{selectedPoints.map(({setKey, points}) => {
const isSelected = setKey === selectedSetKey;
const selectedVariableKey = isSelected && selected
? selected.variableKey
: null;
return (
<RadarCircle
key={setKey}
points={points}
scales={scales}
offsetAngles={offsetAngles}
color={colors[setKey]}
isSelected={isSelected}
selectedVariableKey={selectedVariableKey}
/>
);
})}
</g>
</g>
</svg>
);
}
const [highlightedPoints, regularPoints] = _.partition(
allPoints,
({setKey}) => setKey === highlightedSetKey,
);

return (
<RadarWrapper
variables={data.variables}
width={width}
height={height}
padding={padding}
domainMax={domainMax}
style={style}
onHover={onHover}
highlighted={highlighted}
scales={scales}
backgroundScale={backgroundScale}
offsetAngles={offsetAngles}
voronoiDiagram={voronoiDiagram}
radius={radius}
highlightedPoint={
highlightedPoints.length > 0 ? highlightedPoints[0] : null
}
regularPoints={regularPoints}
colors={colors}
/>
);
}
Loading

0 comments on commit 73b3f99

Please sign in to comment.