react-d3-radar
Version:
React-based Radar chart for D3
127 lines (109 loc) • 3.31 kB
Flow
// @flow
import React from "react";
import { schemeCategory10 } from "d3-scale-chromatic";
import { voronoi } from "d3-voronoi";
import partition from "lodash/partition";
import {
flatMapDeepArray,
forEachArray,
radarPoints,
radiusScales
} from "./utils";
import type { RadarPoint, RadarData } from "./types";
import RadarWrapper from "./RadarWrapper";
type Props = {
data: RadarData,
width: number,
height: number,
padding: number,
domainMax: number,
style?: {},
onHover?: (point: RadarPoint | null) => void,
highlighted: ?RadarPoint,
onAxisLabelClick?: ({ variableKey: string, label: string }) => void,
onAxisLabelMouseover?: ({ variableKey: string, label: string }) => void,
axisLabelTextStyle?: {}
};
function convertData(props) {
const { data, width, height, padding, domainMax } = props;
const innerHeight = height - padding * 2;
const innerWidth = width - padding * 2;
if (innerHeight <= 0 || innerWidth <= 0) {
return null;
}
const radius = Math.min(innerWidth / 2, innerHeight / 2);
const scales = radiusScales(data.variables, domainMax, radius);
const angleSliceRadians = (Math.PI * 2) / data.variables.length;
const offsetAngles = {};
forEachArray(data.variables, ({ key }, i) => {
offsetAngles[key] = angleSliceRadians * i;
});
const allPoints = radarPoints(data, scales, offsetAngles);
const flatPointList = flatMapDeepArray(allPoints, ({ points }) => {
return points;
});
const voronoiDiagram = voronoi()
.x((d: RadarPoint) => d.x + radius + (Math.random() - 0.5))
.y((d: RadarPoint) => d.y + radius + (Math.random() - 0.5))
.size([radius * 2, radius * 2])(flatPointList);
return { allPoints, scales, offsetAngles, voronoiDiagram, radius };
}
export default function Radar(props: Props) {
const {
data,
width,
height,
padding,
domainMax,
style,
onHover,
highlighted,
onAxisLabelClick,
onAxisLabelMouseover,
axisLabelTextStyle
} = props;
const converted = convertData(props);
if (!converted) {
return null;
}
const { allPoints, scales, offsetAngles, radius, voronoiDiagram } = converted;
const highlightedSetKey = highlighted ? highlighted.setKey : null;
const backgroundScale = scales[data.variables[0].key];
const colors = {};
forEachArray(allPoints, ({ setKey, color }, idx) => {
if (color) {
colors[setKey] = color;
} else {
colors[setKey] = schemeCategory10[idx];
}
});
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}
onAxisLabelClick={onAxisLabelClick}
onAxisLabelMouseover={onAxisLabelMouseover}
axisLabelTextStyle={axisLabelTextStyle}
/>
);
}