@data-ui/histogram
Version:
React + d3 library for creating histograms
184 lines (177 loc) • 5.87 kB
JavaScript
import PropTypes from 'prop-types';
import React from 'react';
import { NodeGroup } from 'react-move';
import { chartTheme } from '@data-ui/theme';
import { Group } from '@vx/group';
import { Bar } from '@vx/shape';
import callOrValue from '../../utils/callOrValue';
import { binnedDataShape } from '../../utils/propShapes';
var propTypes = {
rawData: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
// eslint-disable-line react/no-unused-prop-types
binnedData: binnedDataShape,
fill: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
fillOpacity: PropTypes.oneOfType([PropTypes.func, PropTypes.number]),
horizontal: PropTypes.bool,
stroke: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
strokeWidth: PropTypes.oneOfType([PropTypes.func, PropTypes.number]),
valueKey: PropTypes.string,
onClick: PropTypes.func,
// likely injected by parent Histogram
binScale: PropTypes.func,
valueScale: PropTypes.func,
onMouseMove: PropTypes.func,
onMouseLeave: PropTypes.func,
keyAccessor: PropTypes.func
};
var defaultProps = {
rawData: [],
binnedData: [],
binScale: null,
fill: chartTheme.colors.default,
fillOpacity: 0.7,
horizontal: false,
onClick: null,
onMouseMove: null,
onMouseLeave: null,
stroke: '#FFFFFF',
strokeWidth: 1,
valueKey: 'count',
valueScale: null,
keyAccessor: function keyAccessor(d) {
return d.id;
}
};
var INDEX_DELAY_MULTIPLIER = 10;
var getBin = function getBin(d) {
return typeof d.bin === 'undefined' ? d.bin0 : d.bin;
};
var getBin1 = function getBin1(d) {
return typeof d.bin === 'undefined' ? d.bin1 : d.bin;
};
function AnimatedBarSeries(_ref) {
var binnedData = _ref.binnedData,
valueScale = _ref.valueScale,
binScale = _ref.binScale,
horizontal = _ref.horizontal,
keyAccessor = _ref.keyAccessor,
fill = _ref.fill,
fillOpacity = _ref.fillOpacity,
onClick = _ref.onClick,
onMouseMove = _ref.onMouseMove,
onMouseLeave = _ref.onMouseLeave,
stroke = _ref.stroke,
strokeWidth = _ref.strokeWidth,
valueKey = _ref.valueKey;
var maxBarLength = Math.max.apply(Math, valueScale.range()); // compute once and use throughout
var barWidths = binnedData.map(function (_, i) {
return binScale.bandwidth ? binScale.bandwidth() // categorical
: Math.abs(binScale(binnedData[i].bin1) - binScale(binnedData[i].bin0));
} // numeric
);
var getValue = function getValue(d) {
return d[valueKey];
};
var getX = horizontal ? getValue : getBin;
var getY = horizontal ? getBin1 : getValue;
var xScale = horizontal ? valueScale : binScale;
var yScale = horizontal ? binScale : valueScale;
return React.createElement(NodeGroup, {
data: binnedData,
keyAccessor: keyAccessor,
start: function start(d, i) {
return {
x: horizontal ? 0 : xScale(getX(d)),
y: horizontal ? yScale(getY(d)) : maxBarLength,
fill: d.fill || callOrValue(fill, d, i),
width: horizontal ? 0 : barWidths[i],
height: horizontal ? barWidths[i] : 0
};
},
enter: function enter(d, i) {
return {
x: [horizontal ? 0 : xScale(getX(d))],
y: [yScale(getY(d))],
width: [horizontal ? xScale(getX(d)) : barWidths[i]],
height: [horizontal ? barWidths[i] : maxBarLength - yScale(getY(d))],
fill: [d.fill || callOrValue(fill, d, i)],
stroke: [d.stroke || callOrValue(stroke, d, i)],
timing: {
duration: 300,
delay: INDEX_DELAY_MULTIPLIER * i
}
};
},
update: function update(d, i) {
return {
x: [horizontal ? 0 : xScale(getX(d))],
y: [yScale(getY(d))],
width: [horizontal ? xScale(getX(d)) : barWidths[i]],
height: [horizontal ? barWidths[i] : maxBarLength - yScale(getY(d))],
fill: [d.fill || callOrValue(fill, d, i)],
stroke: [d.stroke || callOrValue(stroke, d, i)],
timing: {
duration: 300,
delay: INDEX_DELAY_MULTIPLIER * i
}
};
},
leave: function leave(d, i) {
return {
x: horizontal ? 0 : xScale(getX(d)),
y: horizontal ? yScale(getY(d)) : maxBarLength,
width: horizontal ? 0 : barWidths[i],
height: horizontal ? barWidths[i] : 0,
timing: {
duration: 300,
delay: INDEX_DELAY_MULTIPLIER / 2 * i
}
};
}
}, function (data) {
return React.createElement(Group, null, data.map(function (modifiedDatum, i) {
var key = modifiedDatum.key,
rawDatum = modifiedDatum.data,
d = modifiedDatum.state;
return React.createElement(Bar, {
key: "bar" + key,
x: d.x,
y: d.y,
width: d.width,
height: d.height,
fill: d.fill,
stroke: d.stroke,
fillOpacity: typeof fillOpacity === 'undefined' ? callOrValue(fillOpacity, rawDatum, i) : fillOpacity,
strokeWidth: rawDatum.strokeWidth || callOrValue(strokeWidth, rawDatum, i),
onClick: onClick && function () {
return function (event) {
onClick({
event: event,
datum: rawDatum,
data: binnedData,
color: d.fill,
index: i
});
};
},
onMouseMove: onMouseMove && function () {
return function (event) {
onMouseMove({
event: event,
datum: rawDatum,
data: binnedData,
color: d.fill,
index: i
});
};
},
onMouseLeave: onMouseLeave && function () {
return onMouseLeave;
}
});
}));
});
}
AnimatedBarSeries.propTypes = propTypes;
AnimatedBarSeries.defaultProps = defaultProps;
export default AnimatedBarSeries;