@data-ui/histogram
Version:
React + d3 library for creating histograms
286 lines (240 loc) • 9.91 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.default = exports.propTypes = void 0;
var _propTypes = _interopRequireDefault(require("prop-types"));
var _react = _interopRequireDefault(require("react"));
var _group = require("@vx/group");
var _scale = require("@vx/scale");
var _shared = require("@data-ui/shared");
var _componentIsX = require("../utils/componentIsX");
var _collectBinnedDataFromChildSeries = _interopRequireDefault(require("../utils/collectBinnedDataFromChildSeries"));
var _componentName = _interopRequireDefault(require("../utils/componentName"));
var _computeDomainsFromBins = _interopRequireDefault(require("../utils/computeDomainsFromBins"));
var _getValueKey = _interopRequireDefault(require("../utils/getValueKey"));
var _shallowCompareObjectEntries = _interopRequireDefault(require("../utils/shallowCompareObjectEntries"));
var _propShapes = require("../utils/propShapes");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
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); }
var propTypes = _extends({}, _shared.withTooltipPropTypes, {
ariaLabel: _propTypes.default.string.isRequired,
binValues: _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string])),
binCount: _propTypes.default.number,
binType: _propTypes.default.oneOf(['numeric', 'categorical']),
children: _propTypes.default.node.isRequired,
cumulative: _propTypes.default.bool,
height: _propTypes.default.number.isRequired,
horizontal: _propTypes.default.bool,
limits: _propTypes.default.arrayOf(_propTypes.default.number),
// values outside the limits are ignored
margin: _propTypes.default.shape({
top: _propTypes.default.number,
right: _propTypes.default.number,
bottom: _propTypes.default.number,
left: _propTypes.default.number
}),
normalized: _propTypes.default.bool,
renderTooltip: _propTypes.default.func,
theme: _propShapes.themeShape,
width: _propTypes.default.number.isRequired,
valueAccessor: _propTypes.default.func
});
exports.propTypes = propTypes;
var defaultProps = {
binCount: 10,
binType: 'numeric',
binValues: null,
cumulative: false,
horizontal: false,
limits: null,
margin: {
top: 32,
right: 32,
bottom: 64,
left: 64
},
normalized: false,
renderTooltip: null,
theme: {},
valueAccessor: function valueAccessor(d) {
return d;
}
};
var Histogram =
/*#__PURE__*/
function (_React$PureComponent) {
_inheritsLoose(Histogram, _React$PureComponent);
function Histogram(props) {
var _this;
_this = _React$PureComponent.call(this, props) || this;
_this.state = _this.getStateFromProps(props);
return _this;
}
var _proto = Histogram.prototype;
_proto.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
var _this2 = this;
var shouldComputeBinsAndScales = false; // eslint-disable-next-line react/destructuring-assignment
if (['width', 'height', 'children'].some(function (prop) {
return _this2.props[prop] !== nextProps[prop];
})) {
shouldComputeBinsAndScales = true;
}
if (['margin'].some( // eslint-disable-next-line react/destructuring-assignment
function (prop) {
return !(0, _shallowCompareObjectEntries.default)(_this2.props[prop], nextProps[prop]);
})) {
shouldComputeBinsAndScales = true;
}
if (shouldComputeBinsAndScales) this.setState(this.getStateFromProps(nextProps));
};
_proto.getStateFromProps = function getStateFromProps(props) {
var dimensions = this.getDimmensions(props);
var binsByIndex = this.getBinnedData(props);
var scales = this.getScales(props, binsByIndex, dimensions);
return _extends({
binsByIndex: binsByIndex
}, dimensions, scales);
};
_proto.getDimmensions = function getDimmensions(props) {
var _ref = props || this.props,
margin = _ref.margin,
width = _ref.width,
height = _ref.height;
var completeMargin = _extends({}, defaultProps.margin, margin);
return {
margin: completeMargin,
innerHeight: height - completeMargin.top - completeMargin.bottom,
innerWidth: width - completeMargin.left - completeMargin.right
};
};
_proto.getBinnedData = function getBinnedData(props) {
var _ref2 = props || this.props,
children = _ref2.children,
binCount = _ref2.binCount,
binType = _ref2.binType,
binValues = _ref2.binValues,
limits = _ref2.limits,
valueAccessor = _ref2.valueAccessor;
return (0, _collectBinnedDataFromChildSeries.default)({
children: children,
binCount: binCount,
binType: binType,
binValues: binValues,
limits: limits,
valueAccessor: valueAccessor
});
};
_proto.getScales = function getScales(props, binsByIndex, _ref3) {
var innerHeight = _ref3.innerHeight,
innerWidth = _ref3.innerWidth;
var _ref4 = props || this.props,
binType = _ref4.binType,
binValues = _ref4.binValues,
cumulative = _ref4.cumulative,
horizontal = _ref4.horizontal,
normalized = _ref4.normalized;
var binScaleFunc = binType === 'numeric' ? _scale.scaleLinear : _scale.scaleBand;
var valueKey = (0, _getValueKey.default)({
normalized: normalized,
cumulative: cumulative
});
var _computeDomainsFromBi = (0, _computeDomainsFromBins.default)({
binsByIndex: binsByIndex,
binType: binType,
binValues: binValues,
valueKey: valueKey
}),
binDomain = _computeDomainsFromBi.binDomain,
valueDomain = _computeDomainsFromBi.valueDomain;
var binRange = horizontal ? [innerHeight, 0] : [0, innerWidth];
var valueRange = horizontal ? [0, innerWidth] : [innerHeight, 0]; // when viewing categorical data horizontally it is more natural to
// read alphabetical from top down
if (horizontal && binType === 'categorical') binRange.reverse();
return {
binScale: binScaleFunc({
range: binRange,
domain: binDomain
}),
valueScale: (0, _scale.scaleLinear)({
range: valueRange,
domain: valueDomain
}),
valueKey: valueKey
};
};
_proto.render = function render() {
var renderTooltip = this.props.renderTooltip;
if (renderTooltip) {
return _react.default.createElement(_shared.WithTooltip, {
renderTooltip: renderTooltip
}, _react.default.createElement(Histogram, _extends({}, this.props, {
renderTooltip: null
})));
}
var _this$props = this.props,
ariaLabel = _this$props.ariaLabel,
binType = _this$props.binType,
binValues = _this$props.binValues,
children = _this$props.children,
height = _this$props.height,
horizontal = _this$props.horizontal,
onMouseLeave = _this$props.onMouseLeave,
onMouseMove = _this$props.onMouseMove,
theme = _this$props.theme,
valueAccessor = _this$props.valueAccessor,
width = _this$props.width;
var _this$state = this.state,
binsByIndex = _this$state.binsByIndex,
binScale = _this$state.binScale,
innerHeight = _this$state.innerHeight,
innerWidth = _this$state.innerWidth,
margin = _this$state.margin,
valueKey = _this$state.valueKey,
valueScale = _this$state.valueScale;
return _react.default.createElement("svg", {
"aria-label": ariaLabel,
role: "img",
width: width,
height: height
}, _react.default.createElement(_group.Group, {
left: margin.left,
top: margin.top
}, _react.default.Children.map(children, function (Child, index) {
var name = (0, _componentName.default)(Child);
if ((0, _componentIsX.isSeries)(name)) {
var binnedData = binsByIndex[index];
return _react.default.cloneElement(Child, {
binScale: binScale,
binType: binType,
binnedData: binnedData,
horizontal: horizontal,
valueAccessor: valueAccessor,
valueKey: valueKey,
valueScale: valueScale,
onMouseLeave: onMouseLeave,
onMouseMove: onMouseMove
});
} else if ((0, _componentIsX.isAxis)(name)) {
var styleKey = name[0].toLowerCase();
var binOrValue = name === 'XAxis' && !horizontal || name === 'YAxis' && horizontal ? 'bin' : 'value';
var tickValues = Child.props.tickValues || (binOrValue === 'bin' && binValues ? binValues : null);
return _react.default.cloneElement(Child, {
top: name === 'YAxis' || Child.props.orientation === 'top' ? 0 : innerHeight,
left: name === 'XAxis' || Child.props.orientation === 'left' ? 0 : innerWidth,
label: Child.props.label || (binOrValue === 'value' ? valueKey : null),
scale: binOrValue === 'value' ? valueScale : binScale,
axisStyles: _extends({}, theme[styleKey + "AxisStyles"], Child.props.axisStyles),
tickStyles: _extends({}, theme[styleKey + "TickStyles"], Child.props.tickStyles),
tickValues: tickValues
});
}
return Child;
})));
};
return Histogram;
}(_react.default.PureComponent);
Histogram.propTypes = propTypes;
Histogram.defaultProps = defaultProps;
Histogram.displayName = 'Histogram';
var _default = Histogram;
exports.default = _default;