lucid-ui
Version:
A UI component library from AppNexus.
225 lines (205 loc) • 9.79 kB
JavaScript
import _partial from "lodash/partial";
import _get from "lodash/get";
import _map from "lodash/map";
import _isEmpty from "lodash/isEmpty";
import _keys from "lodash/keys";
import _identity from "lodash/identity";
import _noop from "lodash/noop";
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import React from 'react';
import PropTypes from 'react-peek/prop-types';
import { lucidClassNames } from '../../util/style-helpers';
import { omitProps } from '../../util/component-types';
import * as d3Shape from 'd3-shape';
import * as chartConstants from '../../constants/charts';
import { buildModernHybridComponent } from '../../util/state-management';
import Line from '../../components/Line/Line';
import { ToolTipDumb as ToolTip } from '../../components/ToolTip/ToolTip';
import reducers from './PieChart.reducers';
var cx = lucidClassNames.bind('&-PieChart');
var string = PropTypes.string,
number = PropTypes.number,
arrayOf = PropTypes.arrayOf,
bool = PropTypes.bool,
shape = PropTypes.shape,
object = PropTypes.object,
func = PropTypes.func;
var DONUT_WIDTH = 15;
var HOVER_SCALE = 1.1; // duplicated in .less file
var INNER_RADIUS = 0.5;
var defaultProps = {
height: 200,
width: 200,
// duplicated because `statics` aren't available during getDefaultProps
margin: {
top: 10,
right: 10,
bottom: 10,
left: 10
},
palette: chartConstants.PALETTE_7,
hasToolTips: true,
hasStroke: true,
isDonut: false,
donutWidth: DONUT_WIDTH,
ToolTip: ToolTip.defaultProps,
isHovering: false,
hoveringIndex: 0,
onMouseOver: _noop,
onMouseOut: _noop,
xAxisField: 'x',
xAxisFormatter: _identity,
yAxisField: 'y',
yAxisFormatter: _identity
};
var PieChart = function PieChart(props) {
var style = props.style,
className = props.className,
height = props.height,
width = props.width,
marginOriginal = props.margin,
data = props.data,
hasToolTips = props.hasToolTips,
hasStroke = props.hasStroke,
palette = props.palette,
colorMap = props.colorMap,
isDonut = props.isDonut,
donutWidth = props.donutWidth,
toolTipProps = props.ToolTip,
isHovering = props.isHovering,
hoveringIndex = props.hoveringIndex,
xAxisField = props.xAxisField,
xAxisFormatter = props.xAxisFormatter,
yAxisField = props.yAxisField,
yAxisFormatter = props.yAxisFormatter,
passThroughs = _objectWithoutProperties(props, ["style", "className", "height", "width", "margin", "data", "hasToolTips", "hasStroke", "palette", "colorMap", "isDonut", "donutWidth", "ToolTip", "isHovering", "hoveringIndex", "xAxisField", "xAxisFormatter", "yAxisField", "yAxisFormatter"]);
var margin = _objectSpread(_objectSpread({}, PieChart.MARGIN), marginOriginal);
var svgClasses = cx(className, '&');
var pieChartProps = omitProps(omitProps(passThroughs, undefined, _keys(ToolTip.propTypes)), undefined, _keys(PieChart.propTypes)); // TODO: Consider displaying something specific when there is no data,
// perhaps a loading indicator.
if (_isEmpty(data) || width < 1 || height < 1) {
return /*#__PURE__*/React.createElement("svg", _extends({}, pieChartProps, {
style: style,
className: svgClasses,
width: width,
height: height
}));
}
var innerWidth = width - margin.left - margin.right;
var innerHeight = height - margin.top - margin.bottom;
var outerRadius = Math.min(innerWidth, innerHeight) / 2;
var pie = d3Shape.pie().sort(null); // needed to put the slices in proper order
var pieData = pie(_map(data, yAxisField));
var arc = d3Shape.arc().innerRadius(isDonut ? outerRadius - donutWidth : INNER_RADIUS).outerRadius(outerRadius); // Useful for capturing hovers when we're in donut mode
var arcFull = d3Shape.arc().innerRadius(0).outerRadius(outerRadius);
var handleMouseOut = function handleMouseOut(_ref) {
var event = _ref.event;
props.onMouseOut({
props: props,
event: event
});
};
var handleMouseOver = function handleMouseOver(index, event) {
props.onMouseOver(index, {
props: props,
event: event
});
};
return /*#__PURE__*/React.createElement("svg", _extends({}, pieChartProps, {
style: style,
className: svgClasses,
width: width,
height: height
}), /*#__PURE__*/React.createElement(ToolTip, _extends({}, toolTipProps, {
isLight: true,
isExpanded: hasToolTips && isHovering,
onMouseOver: _noop,
onMouseOut: handleMouseOut
}), /*#__PURE__*/React.createElement(ToolTip.Target, {
elementType: "g"
}, /*#__PURE__*/React.createElement("g", {
transform: "translate(".concat(margin.left, ", ").concat(margin.top, ")")
}, /*#__PURE__*/React.createElement("g", {
transform: "translate(".concat(innerWidth / 2, ", ").concat(innerHeight / 2, ")")
}, _map(pieData, function (pieDatum, index) {
/* Even though innerRadius and outerRadius are set when
constructing arc and arcFull, these functions still expect a type
that includes innerRadius and outerRadius */
//@ts-ignore
var arcFullData = arcFull(pieDatum); //@ts-ignore
var arcData = arc(pieDatum);
return /*#__PURE__*/React.createElement("g", {
key: index,
className: cx('&-slice-group', {
'&-slice-group-is-hovering': isHovering && hoveringIndex === index
})
}, /*#__PURE__*/React.createElement(Line, {
key: index,
className: cx('&-slice', {
'&-slice-has-stroke': hasStroke
}),
d: arcData,
color: _get(colorMap, data && data[index][xAxisField] || '', palette[index % palette.length]),
transform: "scale(".concat(isHovering && hoveringIndex === index ? HOVER_SCALE : 1, ")")
}), /*#__PURE__*/React.createElement("path", {
className: cx('&-slice-hover'),
d: arcFullData,
transform: "scale(".concat(HOVER_SCALE, ")"),
onMouseOver: _partial(handleMouseOver, index),
onMouseOut: hasToolTips ? _noop : handleMouseOut
}));
})))), /*#__PURE__*/React.createElement(ToolTip.Title, null, xAxisFormatter(_get(data, "[".concat(hoveringIndex, "].").concat(xAxisField)))), /*#__PURE__*/React.createElement(ToolTip.Body, null, yAxisFormatter(_get(data, "[".concat(hoveringIndex, "].").concat(yAxisField))))));
};
PieChart.displayName = 'PieChart';
PieChart.propTypes = {
style: object,
className: string,
height: number,
width: number,
margin: shape({
top: number,
right: number,
bottom: number,
left: number
}),
data: arrayOf(object),
hasToolTips: bool,
hasStroke: bool,
palette: arrayOf(string),
colorMap: object,
ToolTip: shape(ToolTip.propTypes),
isDonut: bool,
isHovering: bool,
hoveringIndex: number,
onMouseOver: func,
onMouseOut: func,
donutWidth: number,
xAxisField: string,
xAxisFormatter: func,
yAxisField: string,
yAxisFormatter: func
};
PieChart.peek = {
description: "\n\t\t\t\t\tPie charts are used for categorical data when you want to show the\n\t\t\t\t\trelative size of each category to the whole. We use similar \"x\" and \"y\"\n\t\t\t\t\tterms to keep parity with the other charts even though pie charts are\n\t\t\t\t\treally just key value based.\n\t\t\t\t",
categories: ['visualizations', 'charts'],
madeFrom: ['ToolTip']
};
PieChart.MARGIN = {
top: 10,
right: 10,
bottom: 10,
left: 10
};
PieChart.DONUT_WIDTH = DONUT_WIDTH;
PieChart.HOVER_SCALE = HOVER_SCALE;
PieChart.reducers = reducers;
PieChart.defaultProps = defaultProps;
export default buildModernHybridComponent(PieChart, {
reducers: reducers
});
export { PieChart as PieChartDumb };