react-liquidchart
Advanced tools
Comparing version 0.1.14 to 0.2.0
@@ -8,13 +8,6 @@ import React from 'react'; | ||
it('should render path', () => { | ||
/* | ||
const wrapper = mount( | ||
<Liquid | ||
width={400} | ||
height={400} | ||
/> | ||
<Liquid />, | ||
); | ||
expect(wrapper.find('path').length).toBeGreaterThan(1); | ||
*/ | ||
}); | ||
}); |
@@ -13,10 +13,12 @@ 'use strict'; | ||
var _d3Timer = require('d3-timer'); | ||
var _cloneChildren = require('react-offcharts-core/Utils/cloneChildren'); | ||
var _d3Shape = require('d3-shape'); | ||
var _cloneChildren2 = _interopRequireDefault(_cloneChildren); | ||
var _d3Ease = require('d3-ease'); | ||
var _arcDimension = require('react-offcharts-core/Helpers/arcDimension'); | ||
var ease = _interopRequireWildcard(_d3Ease); | ||
var _arcDimension2 = _interopRequireDefault(_arcDimension); | ||
var _numbers = require('react-offcharts-core/Utils/numbers'); | ||
var _d3Selection = require('d3-selection'); | ||
@@ -26,18 +28,18 @@ | ||
var _d3Interpolate = require('d3-interpolate'); | ||
var _d3Ease = require('d3-ease'); | ||
var _d3Color = require('d3-color'); | ||
var ease = _interopRequireWildcard(_d3Ease); | ||
var d3Color = _interopRequireWildcard(_d3Color); | ||
require('d3-transition'); | ||
var _ReactIf = require('./ReactIf'); | ||
var _d3Interpolate = require('d3-interpolate'); | ||
var _ReactIf2 = _interopRequireDefault(_ReactIf); | ||
var _constants = require('../Helpers/constants'); | ||
var _Text = require('./Text'); | ||
var ch = _interopRequireWildcard(_constants); | ||
var _Text2 = _interopRequireDefault(_Text); | ||
var _dimensions = require('../Helpers/dimensions'); | ||
var dh = _interopRequireWildcard(_dimensions); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -53,28 +55,23 @@ | ||
/* | ||
PropType for fill and stroke.. | ||
*/ | ||
var fillStroke = _react.PropTypes.shape({ | ||
fill: _react.PropTypes.string, | ||
stroke: _react.PropTypes.string | ||
}); | ||
var sample = 60; | ||
var Liquid = function (_Component) { | ||
_inherits(Liquid, _Component); | ||
var LiquidChart = function (_Component) { | ||
_inherits(LiquidChart, _Component); | ||
function Liquid(props) { | ||
_classCallCheck(this, Liquid); | ||
function LiquidChart() { | ||
_classCallCheck(this, LiquidChart); | ||
var _this = _possibleConstructorReturn(this, (Liquid.__proto__ || Object.getPrototypeOf(Liquid)).call(this)); | ||
return _possibleConstructorReturn(this, (LiquidChart.__proto__ || Object.getPrototypeOf(LiquidChart)).apply(this, arguments)); | ||
if (props.innerBound > props.outerBound) { | ||
console.warn(ch.INNER_BIGGER_THAN_OUTER); | ||
} else if (props.outerBound > 1) { | ||
console.warn(ch.OUTER_BIGGER_THAN_ONE); | ||
} | ||
_this.iter = 0; | ||
return _this; | ||
} | ||
_createClass(LiquidChart, [{ | ||
_createClass(Liquid, [{ | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
if (this.props.animate) { | ||
this.animate(); | ||
return; | ||
} | ||
this.draw(); | ||
this.renderChart(); | ||
} | ||
@@ -84,138 +81,216 @@ }, { | ||
value: function componentDidUpdate(prevProps, prevState) { | ||
if (this.props.animate) { | ||
this.animate(); | ||
return; | ||
this.renderChart(); | ||
} | ||
}, { | ||
key: 'getEasing', | ||
value: function getEasing() { | ||
var animationEase = ease[this.props.animationEase]; | ||
if (typeof animationEase === 'function') { | ||
return animationEase; | ||
} | ||
this.draw(); | ||
return ease.easeCubicInOut; | ||
} | ||
}, { | ||
key: 'setRes', | ||
value: function setRes() { | ||
// set an array filled with zeros, | ||
// we are just going to use the index anyways | ||
this.arr = new Array(100); | ||
// get the clip path | ||
this.wave = (0, _d3Selection.select)(this.clipPath).datum([this.props.value]); | ||
// get the tspan | ||
this.text = (0, _d3Selection.select)(this.container).selectAll('text').selectAll('tspan.value'); | ||
key: 'getAnimationTime', | ||
value: function getAnimationTime() { | ||
var animationTime = this.props.animationTime; | ||
var height = this.props.height * (this.props.innerRadius - this.props.margin) / 2; | ||
// to animate the wave we need to set the width of the path double the diameter | ||
// of the liquid. | ||
this.x = (0, _d3Scale.scaleLinear)().range([-this.liquidRadius * 2, this.liquidRadius * 2]).domain([0, 100]); | ||
this.y = (0, _d3Scale.scaleLinear)().range([height, -height]).domain([0, 100]); | ||
if (animationTime === undefined) { | ||
return 2000; | ||
} | ||
return animationTime; | ||
} | ||
}, { | ||
key: 'draw', | ||
value: function draw() { | ||
key: 'animateBackAndForth', | ||
value: function animateBackAndForth() { | ||
var _this2 = this; | ||
// ready the chart | ||
this.setRes(); | ||
// some basic trig | ||
var val = (0, _d3Shape.area)().x(function (d, i) { | ||
return _this2.x(i); | ||
}).y0(function (d, i) { | ||
return _this2.y(_this2.props.amplitude * Math.sin(i / _this2.props.frequency) + _this2.props.value); | ||
}).y1(function (d) { | ||
return _this2.props.height / 2; | ||
// Set the sampling array to a new array of X times undefines | ||
// does not matter because we only use zeros | ||
var arr = new Array(ch.SAMPLING); | ||
// Get the container element | ||
var container = (0, _d3Selection.select)(this.container); | ||
// select the clippath that is going to be animated | ||
var wave = container.select('clipPath').select('path'); | ||
// Get the easing type, if the user misspelled the easing or | ||
var animationEase = this.getEasing(); | ||
// Get the animationtime | ||
var animationTime = this.getAnimationTime(); | ||
// get the wavescale | ||
var waveScale = dh.getWaveScaleLimit(this.props); | ||
// get the scales and the area function | ||
var _dh$getWaveArea = dh.getWaveArea(this.props), | ||
waveArea = _dh$getWaveArea.waveArea, | ||
x = _dh$getWaveArea.x, | ||
y = _dh$getWaveArea.y; // { waveArea, x, y, w, h } | ||
var _dh$getBackAndForth = dh.getBackAndForth(), | ||
forthAmplitude = _dh$getBackAndForth.forthAmplitude, | ||
backAmplitude = _dh$getBackAndForth.backAmplitude, | ||
forthFrequency = _dh$getBackAndForth.forthFrequency, | ||
backFrequency = _dh$getBackAndForth.backFrequency; | ||
var textValue = container.selectAll('.' + ch.TEXT_VALUE); | ||
var textDecimal = container.selectAll('.' + ch.TEXT_DECIMAL); | ||
var animation = function animation() { | ||
wave.transition().duration(_this2.props.animationWavesTime).ease(ease.easeSinInOut).attrTween('d', function () { | ||
wave.node().M = 1; | ||
return function (t1) { | ||
var amp = forthAmplitude(t1); | ||
var freq = forthFrequency(t1); | ||
var ws = amp * waveScale(_this2.props.value); | ||
waveArea.y0(function (d, i) { | ||
return y(dh.sine(ws, i, _this2.props.frequency, freq) + _this2.props.value); | ||
}); | ||
wave.node().A = amp; | ||
wave.node().F = freq; | ||
return waveArea(arr); | ||
}; | ||
}).transition().duration(_this2.props.animationWavesTime).ease(ease.easeSinInOut).attrTween('d', function () { | ||
wave.node().M = 0; | ||
return function (t1) { | ||
var amp = backAmplitude(t1); | ||
var freq = backFrequency(t1); | ||
var ws = amp * waveScale(_this2.props.value); | ||
waveArea.y0(function (d, i) { | ||
return y(dh.sine(ws, i, _this2.props.frequency, freq) + _this2.props.value); | ||
}); | ||
wave.node().A = amp; | ||
wave.node().F = freq; | ||
return waveArea(arr); | ||
}; | ||
}).on('end', function () { | ||
animation(); | ||
}); | ||
}; | ||
wave.transition().duration(animationTime).ease(animationEase).attrTween('d', function () { | ||
var interVal = (0, _d3Interpolate.interpolate)(wave.node().old || 0, _this2.props.value); | ||
var _dh$getWaveValueMovem = dh.getWaveValueMovement(wave.node()), | ||
amplitudeScale = _dh$getWaveValueMovem.amplitudeScale, | ||
frequencyScale = _dh$getWaveValueMovem.frequencyScale; | ||
var updateNum = function updateNum(val) { | ||
var value = (0, _numbers.round)(val); | ||
var sp = (0, _numbers.splitNumber)(value, _this2.props.deliminator); | ||
textValue.text(sp.number); | ||
textDecimal.text('.' + sp.fraction); | ||
}; | ||
return function (t) { | ||
var val = interVal(t); | ||
var amp = amplitudeScale(t); | ||
var freq = frequencyScale(t); | ||
var ws = amp * waveScale(val); | ||
waveArea.y0(function (d, i) { | ||
return y(dh.sine(ws, i, _this2.props.frequency, freq) + val); | ||
}); | ||
wave.node().old = val; | ||
wave.node().A = amp; | ||
wave.node().F = freq; | ||
updateNum(val); | ||
return waveArea(arr); | ||
}; | ||
}).on('end', function () { | ||
animation(); | ||
}); | ||
// set the clip path d attribute | ||
this.wave.attr('d', val(this.arr)); | ||
// set the text to the rounded value | ||
// decimal formatting todo | ||
this.text.text(Math.round(this.props.value)); | ||
} | ||
}, { | ||
key: 'animate', | ||
value: function animate() { | ||
key: 'animateValue', | ||
value: function animateValue() { | ||
var _this3 = this; | ||
// set the ease | ||
var easeFn = ease[this.props.ease] ? ease[this.props.ease] : ease.easeCubicInOut; | ||
// ready the chart and do calculations | ||
this.setRes(); | ||
var val = (0, _d3Shape.area)().x(function (d, i) { | ||
return _this3.x(i); | ||
}).y1(function (d) { | ||
return _this3.props.height / 2; | ||
}); | ||
// reduce the wave when it's close to 0 or 100 | ||
var waveScale = void 0; | ||
if (this.props.waveScaleLimit) { | ||
waveScale = (0, _d3Scale.scaleLinear)().range([0, this.props.amplitude, 0]).domain([0, 50, 100]); | ||
} else { | ||
waveScale = (0, _d3Scale.scaleLinear)().range([this.props.amplitude, this.props.amplitude]).domain([0, 50, 100]); | ||
} | ||
// Set the sampling array to a new array of X times undefines | ||
// does not matter because we only use zeros | ||
var arr = new Array(ch.SAMPLING); | ||
if (this.props.animateWaves) { | ||
this.animateWave(); | ||
} | ||
// the d3 timer goes from from 0 to 1 | ||
var time = (0, _d3Scale.scaleLinear)().range([0, 1]).domain([0, this.props.animationTime]); | ||
// if the wave does not have old value then interpolate from 0 to value else old to value | ||
var interpolateValue = (0, _d3Interpolate.interpolate)(this.wave.node().old || 0, this.props.value); | ||
// Get the container element | ||
var container = (0, _d3Selection.select)(this.container); | ||
// start animation | ||
var animationTimer = (0, _d3Timer.timer)(function (t) { | ||
// set the easing | ||
var animate = easeFn(time(t)); | ||
var value = interpolateValue(animate); | ||
// calculate the wave | ||
val.y0(function (d, i) { | ||
return _this3.y(waveScale(value) * Math.sin(i / _this3.props.frequency) + value); | ||
}); | ||
// set the text value | ||
_this3.text.text(Math.round(value)); | ||
// set the wave data attribute | ||
_this3.wave.attr('d', val(_this3.arr)); | ||
// the transition has ended | ||
if (t >= _this3.props.animationTime) { | ||
// stop the timer | ||
animationTimer.stop(); | ||
// Make sure that the animation stops in the right place | ||
// we set 1 as the interpolation parameter | ||
val.y0(function (d, i) { | ||
return _this3.y(waveScale(_this3.props.value) * Math.sin(i / _this3.props.frequency) + interpolateValue(1)); | ||
// select the clippath that is going to be animated | ||
var wave = container.select('clipPath').select('path'); | ||
// Get the easing type, if the user misspelled the easing or | ||
var animationEase = this.getEasing(); | ||
// Get the animationtime | ||
var animationTime = this.getAnimationTime(); | ||
// get the wavescale | ||
var waveScale = dh.getWaveScaleLimit(this.props); | ||
// get the areafunction and dimensions | ||
var _dh$getWaveArea2 = dh.getWaveArea(this.props), | ||
waveArea = _dh$getWaveArea2.waveArea, | ||
x = _dh$getWaveArea2.x, | ||
y = _dh$getWaveArea2.y; // { waveArea, x, y, w, h } | ||
// get the text variables | ||
var textValue = container.selectAll('.' + ch.TEXT_VALUE); | ||
var textDecimal = container.selectAll('.' + ch.TEXT_DECIMAL); | ||
wave.transition().duration(animationTime).ease(animationEase).attrTween('d', function () { | ||
var interVal = (0, _d3Interpolate.interpolate)(wave.node().old || 0, _this3.props.value); | ||
var updateNum = function updateNum(val) { | ||
var value = (0, _numbers.round)(val); | ||
var sp = (0, _numbers.splitNumber)(value, '.'); | ||
textValue.text(sp.number); | ||
textDecimal.text('.' + sp.fraction); | ||
}; | ||
return function (t) { | ||
var val = interVal(t); | ||
var ws = waveScale(val); | ||
waveArea.y0(function (d, i) { | ||
return y(dh.sine(ws, i, _this3.props.frequency, 0) + val); | ||
}); | ||
_this3.text.text(Math.round(interpolateValue(1))); | ||
_this3.wave.attr('d', val(_this3.arr)); | ||
// if the onEnd prop is set then call the function | ||
if (_this3.props.onEnd !== undefined) { | ||
_this3.props.onEnd(); | ||
} | ||
} | ||
wave.node().old = val; | ||
updateNum(val); | ||
return waveArea(arr); | ||
}; | ||
}); | ||
// Store the old node value so that we can animate from | ||
// that point again | ||
this.wave.node().old = this.props.value; | ||
} | ||
// animate the wave from 0 to liquidRadius repeat | ||
// not perfect but works | ||
}, { | ||
key: 'animateWave', | ||
value: function animateWave() { | ||
var _this4 = this; | ||
key: 'animate', | ||
value: function animate() { | ||
if (this.props.animationWavesTime) { | ||
this.animateBackAndForth(); | ||
return; | ||
} | ||
this.animateValue(); | ||
} | ||
}, { | ||
key: 'draw', | ||
value: function draw() { | ||
var arr = new Array(ch.SAMPLING); | ||
var container = (0, _d3Selection.select)(this.container); | ||
var el = container.select('clipPath').select('path'); | ||
var textValue = container.selectAll('.' + ch.TEXT_VALUE); | ||
var decimalValue = container.selectAll('.' + ch.TEXT_DECIMAL); | ||
decimalValue.text('.3'); | ||
textValue.text(parseInt(this.props.value, 10)); | ||
el.attr('d', dh.getWave(this.props)(arr)); | ||
} | ||
}, { | ||
key: 'renderChart', | ||
value: function renderChart() { | ||
var shouldAnimate = this.props.animationTime || this.props.animationEase; | ||
// put a lock on animate function | ||
// so it's not called often | ||
if (!this.isOn) { | ||
(function () { | ||
_this4.isOn = true; | ||
var anime = function anime() { | ||
_this4.wave.attr('transform', 'translate(0,0)').transition().duration(_this4.props.animationWavesTime).ease(ease.easeLinear).attrTween('transform', function () { | ||
var i = (0, _d3Interpolate.interpolate)(0, _this4.liquidRadius); | ||
if (shouldAnimate) { | ||
this.animate(); | ||
return; | ||
} | ||
return function (t) { | ||
var yex = 3; | ||
return 'translate(' + i(t) + ',0)'; | ||
}; | ||
}).on('end', function () { | ||
anime(); | ||
}); | ||
}; | ||
anime(); | ||
})(); | ||
} | ||
this.draw(); | ||
} | ||
@@ -225,60 +300,14 @@ }, { | ||
value: function render() { | ||
var _this5 = this; | ||
var _this4 = this; | ||
this.radius = Math.min(this.props.height / 2, this.props.width / 2); | ||
this.liquidRadius = this.radius * (this.props.innerRadius - this.props.margin); | ||
// set the outerArc arc parameters | ||
var outerArc = (0, _d3Shape.arc)().outerRadius(this.props.outerRadius * this.radius).innerRadius(this.props.innerRadius * this.radius).startAngle(0).endAngle(Math.PI * 2); | ||
// set the chart center | ||
var cX = this.props.width / 2; | ||
var cY = this.props.height / 2; | ||
var fillCircle = this.props.liquid.fill; | ||
if (this.props.gradient) { | ||
fillCircle = 'url(#gradient)'; | ||
} | ||
var d = dh.getDimensions(this.props); | ||
return _react2.default.createElement( | ||
'g', | ||
{ | ||
transform: 'translate(' + cX + ',' + cY + ')', | ||
ref: function ref(c) { | ||
_this5.container = c; | ||
} | ||
_this4.container = c; | ||
}, | ||
transform: 'translate(' + d.cx + ',' + d.cy + ')' | ||
}, | ||
_react2.default.createElement( | ||
'defs', | ||
null, | ||
_react2.default.createElement( | ||
'clipPath', | ||
{ id: 'clip' }, | ||
_react2.default.createElement('path', { ref: function ref(c) { | ||
_this5.clipPath = c; | ||
} }) | ||
) | ||
), | ||
(0, _Text2.default)(this.props, this.props.number), | ||
_react2.default.createElement( | ||
'g', | ||
{ clipPath: 'url(#clip)' }, | ||
_react2.default.createElement('circle', { | ||
r: this.liquidRadius, | ||
fill: fillCircle | ||
}), | ||
(0, _Text2.default)(this.props, this.props.liquidNumber) | ||
), | ||
_react2.default.createElement('path', { | ||
d: outerArc(), | ||
fill: this.props.outerArcStyle.fill, | ||
stroke: this.props.outerArcStyle.stroke | ||
}), | ||
_react2.default.createElement('circle', { | ||
r: this.radius, | ||
fill: 'rgba(0,0,0,0)', | ||
stroke: 'rgba(0,0,0,0)', | ||
style: { pointerEvents: 'all' }, | ||
onClick: function onClick() { | ||
_this5.props.onClick(); | ||
} | ||
}) | ||
(0, _cloneChildren2.default)(this.props, d) | ||
); | ||
@@ -288,80 +317,22 @@ } | ||
return LiquidChart; | ||
return Liquid; | ||
}(_react.Component); | ||
LiquidChart.propTypes = { | ||
// a percentage from 0 to 100 | ||
value: _react.PropTypes.number, | ||
// boolean if true then animate | ||
animate: _react.PropTypes.bool, | ||
// comes from Chart parent | ||
width: _react.PropTypes.number, | ||
// comes from Chart parent | ||
height: _react.PropTypes.number, | ||
// innerRadius | ||
innerRadius: _react.PropTypes.number, | ||
// outer radius | ||
outerRadius: _react.PropTypes.number, | ||
// margin between inner liquid and innerRadius | ||
margin: _react.PropTypes.number, | ||
// callback function called when animation is done | ||
onEnd: _react.PropTypes.func, | ||
// string | ||
ease: _react.PropTypes.string, | ||
// animation Time | ||
Liquid.propTypes = { | ||
value: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.number]), | ||
outerBound: _react.PropTypes.number, | ||
innerBound: _react.PropTypes.number, | ||
animationEase: _react.PropTypes.string, | ||
animationTime: _react.PropTypes.number, | ||
// animation wave time | ||
animationWavesTime: _react.PropTypes.number, | ||
// The fill and stroke for the outer arc | ||
outerArcStyle: fillStroke, | ||
// The fill and stroke for the liquid | ||
liquid: fillStroke, | ||
// The fill and stroke for the number part that | ||
// is drenched in liquid | ||
liquidNumber: fillStroke, | ||
// the fill and stroke of the number that is not drenched in liquid | ||
number: fillStroke, | ||
// the wave amplitude | ||
amplitude: _react.PropTypes.number, | ||
// the wave frequncy inverse, the higer the number the fewer the waves | ||
frequency: _react.PropTypes.number, | ||
// scaling the wave when it´s close to 0 or 100 | ||
waveScaleLimit: _react.PropTypes.bool, | ||
// on click | ||
onClick: _react.PropTypes.func, | ||
// if true then animate waves | ||
animateWaves: _react.PropTypes.bool, | ||
// 1 for linear 2 for radial | ||
gradient: _react.PropTypes.number | ||
frequency: _react.PropTypes.number | ||
}; | ||
LiquidChart.defaultProps = { | ||
value: 65, | ||
animate: false, | ||
outerRadius: 0.9, | ||
innerRadius: 0.8, | ||
margin: 0.025, | ||
ease: 'easeCubicInOut', | ||
animationTime: 2000, | ||
animationWavesTime: 2000, | ||
amplitude: 2, | ||
Liquid.defaultProps = { | ||
amplitude: 1, | ||
liquidMargin: 0.005, | ||
waveScaleLimit: true, | ||
frequency: 4, | ||
outerArcStyle: { | ||
fill: 'rgb(23, 139, 202)' | ||
}, | ||
liquid: { | ||
fill: 'rgb(23, 139, 202)' | ||
}, | ||
liquidNumber: { | ||
fill: 'rgb(164, 219, 248)' | ||
}, | ||
number: { | ||
fill: 'rgb(4, 86, 129)' | ||
}, | ||
offsetX: 1, | ||
offsetY: 1, | ||
onClick: function onClick() {}, | ||
fontSize: '7rem', | ||
smallFontSize: '3rem' | ||
deliminator: '.', | ||
postfix: '' | ||
}; | ||
exports.default = LiquidChart; | ||
exports.default = Liquid; |
@@ -13,6 +13,30 @@ 'use strict'; | ||
var _Chart = require('./Chart'); | ||
var _Chart = require('react-offcharts-core/Components/Chart'); | ||
var _Chart2 = _interopRequireDefault(_Chart); | ||
var _ReactIf = require('react-offcharts-core/Components/ReactIf'); | ||
var _ReactIf2 = _interopRequireDefault(_ReactIf); | ||
var _Gradients = require('react-offcharts-core/Components/Defs/Gradients'); | ||
var _Gradients2 = _interopRequireDefault(_Gradients); | ||
var _guid = require('react-offcharts-core/Utils/guid'); | ||
var _guid2 = _interopRequireDefault(_guid); | ||
var _Clip = require('./Clip'); | ||
var _Clip2 = _interopRequireDefault(_Clip); | ||
var _Text = require('./Text'); | ||
var _Text2 = _interopRequireDefault(_Text); | ||
var _Shape = require('./Shape'); | ||
var _Shape2 = _interopRequireDefault(_Shape); | ||
var _Liquid = require('./Liquid'); | ||
@@ -22,19 +46,124 @@ | ||
var _Gradient = require('./Gradient'); | ||
var _props = require('../Helpers/props'); | ||
var _Gradient2 = _interopRequireDefault(_Gradient); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var gradientId = (0, _guid2.default)(); | ||
var LiquidChart = function LiquidChart(props) { | ||
return _react2.default.createElement( | ||
_Chart2.default, | ||
_extends({ | ||
childRules: false | ||
}, props), | ||
_react2.default.createElement(_Liquid2.default, null), | ||
_react2.default.createElement(_Gradient2.default, null) | ||
{ | ||
width: props.width, | ||
height: props.height, | ||
responsive: props.responsive, | ||
value: props.value, | ||
clipId: (0, _guid2.default)(), | ||
gradientId: gradientId | ||
}, | ||
_react2.default.createElement( | ||
_Liquid2.default, | ||
{ | ||
outerBound: 0.95, | ||
innerBound: 0.85, | ||
amplitude: props.amplitude, | ||
frequency: props.frequency, | ||
waveScaleLimit: props.waveScaleLimit, | ||
animationWavesTime: props.animationWavesTime, | ||
animationTime: props.animationTime, | ||
animationEase: props.animationEase | ||
}, | ||
_react2.default.createElement( | ||
_Shape2.default, | ||
{ | ||
type: props.type, | ||
outerStyle: props.outerStyle, | ||
liquidStyle: props.liquidStyle, | ||
wetStyle: props.wetStyle, | ||
dryStyle: props.dryStyle | ||
}, | ||
_react2.default.createElement(_Text2.default, { | ||
deliminator: props.deliminator, | ||
postfix: props.postfix, | ||
showDecimal: props.showDecimal, | ||
fontSizes: props.fontSizes, | ||
legend: props.legend | ||
}) | ||
), | ||
_react2.default.createElement( | ||
_ReactIf2.default, | ||
{ condition: props.gradient.type, el: _react2.default.createElement('g', null) }, | ||
_react2.default.createElement(_Gradients2.default, _extends({ | ||
id: gradientId | ||
}, props.gradient, { | ||
fill: props.liquidStyle.fill | ||
})) | ||
), | ||
_react2.default.createElement(_Clip2.default, null) | ||
) | ||
); | ||
}; | ||
LiquidChart.propTypes = { | ||
width: _react.PropTypes.number, | ||
height: _react.PropTypes.number, | ||
responsive: _react.PropTypes.bool, | ||
value: _react.PropTypes.number, | ||
amplitude: _react.PropTypes.number, | ||
frequency: _react.PropTypes.number, | ||
waveScaleLimit: _react.PropTypes.bool, | ||
animationWavesTime: _react.PropTypes.number, | ||
animationEase: _react.PropTypes.string, | ||
animationTime: _react.PropTypes.number, | ||
type: _react.PropTypes.number, | ||
outerStyle: _props.fillAndStroke, | ||
liquidStyle: _props.fillAndStroke, | ||
wetStyle: _props.fillAndStroke, | ||
dryStyle: _props.fillAndStroke, | ||
deliminator: _react.PropTypes.string, | ||
postfix: _react.PropTypes.string, | ||
legend: _react.PropTypes.string, | ||
showDecimal: _react.PropTypes.bool, | ||
fontSizes: _react.PropTypes.shape({ | ||
value: _react.PropTypes.number, | ||
decimal: _react.PropTypes.number, | ||
postfix: _react.PropTypes.number, | ||
legend: _react.PropTypes.number | ||
}), | ||
gradient: _react.PropTypes.shape({ | ||
type: _react.PropTypes.number, | ||
r: _react.PropTypes.number, | ||
x: _react.PropTypes.number, | ||
x1: _react.PropTypes.number, | ||
x2: _react.PropTypes.number, | ||
y: _react.PropTypes.number, | ||
y1: _react.PropTypes.number, | ||
y2: _react.PropTypes.number, | ||
cx: _react.PropTypes.number, | ||
cy: _react.PropTypes.number, | ||
fx: _react.PropTypes.number, | ||
fy: _react.PropTypes.number, | ||
stops: _react.PropTypes.arrayOf(_react.PropTypes.node) | ||
}) | ||
}; | ||
LiquidChart.defaultProps = { | ||
outerBound: 0.9, | ||
innerBound: 0.85, | ||
margin: 0.005, | ||
frequency: 2, | ||
amplitude: 4, | ||
waveScaleLimit: true, | ||
outerStyle: { fill: 'rgb(23,139,202)' }, | ||
liquidStyle: { fill: 'rgb(23, 139, 202)' }, | ||
dryStyle: { fill: 'rgb(4, 86, 129)' }, | ||
wetStyle: { fill: 'rgb(164, 219, 248)' }, | ||
gradient: { | ||
x1: 0, | ||
x2: 0, | ||
y1: 100, | ||
y2: 0 | ||
} | ||
}; | ||
exports.default = LiquidChart; |
@@ -1,2 +0,2 @@ | ||
"use strict"; | ||
'use strict'; | ||
@@ -7,30 +7,50 @@ Object.defineProperty(exports, "__esModule", { | ||
var _react = require("react"); | ||
var _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; }; | ||
var _react = require('react'); | ||
var _react2 = _interopRequireDefault(_react); | ||
var _constants = require('../Helpers/constants'); | ||
var ch = _interopRequireWildcard(_constants); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var Text = function Text(props, _ref) { | ||
var fill = _ref.fill, | ||
stroke = _ref.stroke; | ||
var LiquidText = function LiquidText(props) { | ||
return _react2.default.createElement( | ||
"text", | ||
{ | ||
textAnchor: "middle", | ||
fontSize: props.fontSize, | ||
fill: fill, | ||
stroke: stroke, | ||
dy: props.dy, | ||
dx: props.dx | ||
}, | ||
'g', | ||
null, | ||
_react2.default.createElement( | ||
"tspan", | ||
{ className: "value" }, | ||
props.value | ||
'text', | ||
_extends({}, props.style, { textAnchor: 'middle' }), | ||
_react2.default.createElement('tspan', { | ||
className: ch.TEXT_VALUE, | ||
fontSize: props.fontSizes.value * props.radius | ||
}), | ||
props.showDecimal ? _react2.default.createElement('tspan', { | ||
className: ch.TEXT_DECIMAL, | ||
dx: props.fontSizes.decimal * props.radius * -0.01, | ||
fontSize: props.fontSizes.decimal * props.radius | ||
}) : null, | ||
_react2.default.createElement( | ||
'tspan', | ||
{ | ||
className: ch.TEXT_POSTFIX, | ||
fontSize: props.fontSizes.postfix * props.radius | ||
}, | ||
props.postfix | ||
) | ||
), | ||
_react2.default.createElement( | ||
"tspan", | ||
{ fontSize: props.smallFontSize }, | ||
"%" | ||
'text', | ||
_extends({}, props.style, { | ||
className: ch.TEXT_LEGEND, | ||
dy: props.radius * (props.fontSizes.legend + 0.05), | ||
fontSize: props.radius * props.fontSizes.legend, | ||
textAnchor: 'middle' | ||
}), | ||
props.legend | ||
) | ||
@@ -40,10 +60,30 @@ ); | ||
Text.propTypes = { | ||
value: _react.PropTypes.number, | ||
smallFontSize: _react.PropTypes.string, | ||
fontSize: _react.PropTypes.string, | ||
dy: _react.PropTypes.string, | ||
dx: _react.PropTypes.string | ||
var dShape = _react.PropTypes.shape({ | ||
dx: _react.PropTypes.number, | ||
dy: _react.PropTypes.number | ||
}); | ||
LiquidText.propTypes = { | ||
fontSizes: _react.PropTypes.shape({ | ||
value: _react.PropTypes.number, | ||
decimal: _react.PropTypes.number, | ||
postfix: _react.PropTypes.number, | ||
legend: _react.PropTypes.number | ||
}), | ||
radius: _react.PropTypes.number, | ||
postfix: _react.PropTypes.string, | ||
legend: _react.PropTypes.string, | ||
style: _react.PropTypes.shape({}), | ||
showDecimal: _react.PropTypes.bool | ||
}; | ||
exports.default = Text; | ||
LiquidText.defaultProps = { | ||
fontSizes: { | ||
value: 0.5, | ||
decimal: 0.35, | ||
postfix: 0.25, | ||
legend: 0.1 | ||
} | ||
}; | ||
exports.default = LiquidText; |
@@ -6,7 +6,7 @@ 'use strict'; | ||
}); | ||
exports.Gradient = exports.Liquid = exports.Chart = undefined; | ||
exports.Text = exports.Liquid = exports.Clip = exports.Shape = undefined; | ||
var _Chart = require('./Chart/Chart'); | ||
var _Shape = require('./Chart/Shape'); | ||
var _Chart2 = _interopRequireDefault(_Chart); | ||
var _Shape2 = _interopRequireDefault(_Shape); | ||
@@ -17,6 +17,10 @@ var _Liquid = require('./Chart/Liquid'); | ||
var _Gradient = require('./Chart/Gradient'); | ||
var _Clip = require('./Chart/Clip'); | ||
var _Gradient2 = _interopRequireDefault(_Gradient); | ||
var _Clip2 = _interopRequireDefault(_Clip); | ||
var _Text = require('./Chart/Text'); | ||
var _Text2 = _interopRequireDefault(_Text); | ||
var _LiquidChart = require('./Chart/LiquidChart'); | ||
@@ -28,5 +32,6 @@ | ||
exports.Chart = _Chart2.default; | ||
exports.Shape = _Shape2.default; | ||
exports.Clip = _Clip2.default; | ||
exports.Liquid = _Liquid2.default; | ||
exports.Gradient = _Gradient2.default; | ||
exports.Text = _Text2.default; | ||
exports.default = _LiquidChart2.default; |
import React, { Component } from 'react'; | ||
import { render } from 'react-dom'; | ||
import LiquidChart, { Chart, Liquid, Gradient } from '../src/index'; | ||
import LiquidChart from '../src/index'; | ||
class ChartLiquid extends Component { | ||
const style = { | ||
width: '50%', | ||
height: '500px', | ||
}; | ||
class TestLiquid extends Component { | ||
constructor() { | ||
super(); | ||
this.onClick = this.onClick.bind(this); | ||
this.state = ({ | ||
value: (Math.random() * 100), | ||
}); | ||
this.onClickOne = this.onClickOne.bind(this); | ||
this.onClickTwo = this.onClickTwo.bind(this); | ||
this.state = { | ||
valueOne: Math.random() * 100, | ||
valueTwo: Math.random() * 100, | ||
}; | ||
} | ||
onClick() { | ||
this.setState({ | ||
value: (Math.random() * 100), | ||
}); | ||
onClickOne() { | ||
this.setState({ valueOne: Math.random() * 100 }); | ||
} | ||
onClickTwo() { | ||
this.setState({ valueTwo: Math.random() * 100 }); | ||
} | ||
render() { | ||
const Comp = ( | ||
<div | ||
style={{ | ||
width: '100%', | ||
height: '500px', | ||
}} | ||
> | ||
<Chart | ||
responsive | ||
gradient={2} | ||
> | ||
<Liquid | ||
animate | ||
animateWaves | ||
animationWavesTime={4000} | ||
onClick={this.onClick} | ||
return ( | ||
<span> | ||
<div style={style}> | ||
<LiquidChart | ||
responsive | ||
legend="Percentage of Completed Tasks" | ||
value={this.state.valueOne} | ||
amplitude={4} | ||
value={this.state.value} | ||
frequency={2} | ||
animationTime={2000} | ||
animationWavesTime={2250} | ||
showDecimal | ||
gradient={{ | ||
type: 1, | ||
x1: 0, | ||
x2: 0, | ||
y1: 100, | ||
y2: 0, | ||
}} | ||
postfix="%" | ||
legendFontSize={0.1} | ||
/> | ||
<Gradient /> | ||
</Chart> | ||
</div> | ||
</div> | ||
<button onClick={() => { this.onClickOne(); }}>RANDOM</button> | ||
<div style={style}> | ||
<LiquidChart | ||
responsive | ||
legend="Percentage of Completed Tasks" | ||
value={this.state.valueTwo} | ||
waveScaleLimit | ||
amplitude={2} | ||
frequency={4} | ||
/> | ||
</div> | ||
</span> | ||
); | ||
} | ||
const Easy = ( | ||
<div | ||
style={{ | ||
width: '100%', | ||
height: '500px', | ||
}} | ||
> | ||
<LiquidChart | ||
responsive | ||
gradient="liquid" | ||
animate | ||
animateWaves | ||
animationWavesTime={150} | ||
onClick={this.onClick} | ||
amplitude={4} | ||
value={this.state.value} | ||
/> | ||
</div> | ||
); | ||
return ( | ||
Comp | ||
); | ||
} | ||
} | ||
render(<TestLiquid />, document.getElementById('app')); | ||
render(<ChartLiquid />, document.getElementById('app')); | ||
@@ -1,2 +0,2 @@ | ||
Original | ||
Original Copyright Curtis Bratton | ||
@@ -31,3 +31,3 @@ BSD 2-Clause License | ||
Modfications licence | ||
Modfications licence Arnthor Agustsson | ||
@@ -59,4 +59,4 @@ BSD 2-Clause License | ||
Open source under BSD 2-clause | ||
Copyright (c) 2016 Arnthor Agustsson | ||
Copyright (c) 2017 Arnthor Agustsson | ||
All rights reserved. | ||
{ | ||
"name": "react-liquidchart", | ||
"version": "0.1.14", | ||
"version": "0.2.0", | ||
"description": "A Reusable liquid chart made with d3 and react", | ||
"main": "dist/index.js", | ||
"scripts": { | ||
"test": "jest --watch", | ||
"test": "jest", | ||
"test:watch": "jest --watch", | ||
"coverage": "jest --coverage", | ||
"start": "webpack-dev-server --inline --hot", | ||
"coveralls": "npm run coverage | coveralls", | ||
"build": "babel src -d dist" | ||
@@ -32,2 +35,3 @@ }, | ||
"babel-preset-stage-0": "^6.16.0", | ||
"coveralls": "^2.11.15", | ||
"enzyme": "^2.6.0", | ||
@@ -56,8 +60,7 @@ "eslint": "^3.11.1", | ||
"d3-shape": "^1.0.4", | ||
"d3-timer": "^1.0.3", | ||
"d3-transition": "^1.0.3", | ||
"lodash.throttle": "^4.1.1", | ||
"react": "^15.4.1", | ||
"react-dom": "^15.4.1" | ||
"react-dom": "^15.4.1", | ||
"react-offcharts-core": "^0.1.2" | ||
} | ||
} |
185
readme.md
## React-liquidchart | ||
[![Build Status](https://travis-ci.org/arnthor3/react-liquidchart.svg?branch=master)](https://travis-ci.org/arnthor3/react-liquidchart) | ||
[![Coverage Status](https://coveralls.io/repos/github/arnthor3/react-liquidchart/badge.svg?branch=)](https://coveralls.io/github/arnthor3/react-liquidchart?branch=) | ||
A Nice looking liquid chart done with d3 and react, based off http://bl.ocks.org/brattonc/5e5ce9beee483220e2f6 | ||
I will put up an example page when I am finished with all the graph types that I want to do and use - about 4 -5 more. | ||
### Install from NPM | ||
@@ -11,54 +12,7 @@ ```sh | ||
### Example | ||
For now, [this](https://arnthor3.github.io/arnthor3/offcharts) will have to do. | ||
### Usage | ||
``` js | ||
import React, { Component } from 'react'; | ||
import { render } from 'react-dom'; | ||
import { Chart, Liquid, Gradient } from 'react-liquidchart'; | ||
class ChartLiquid extends Component { | ||
constructor() { | ||
super(); | ||
this.onClick = this.onClick.bind(this); | ||
this.state = ({ | ||
value: (Math.random() * 100), | ||
}); | ||
} | ||
onClick() { | ||
this.setState({ | ||
value: (Math.random() * 100), | ||
}); | ||
} | ||
render() { | ||
return ( | ||
<div | ||
style={{ | ||
width: '100%', | ||
height: '500px', | ||
}} | ||
> | ||
<Chart | ||
responsive | ||
gradient="liquid" | ||
> | ||
<Liquid | ||
animate | ||
animateWaves | ||
onClick={this.onClick} | ||
amplitude={4} | ||
value={this.state.value} | ||
/> | ||
<Gradient /> | ||
</Chart> | ||
</div> | ||
); | ||
} | ||
} | ||
render(<ChartLiquid />, document.getElementById('app')); | ||
``` | ||
### Simpler version but with no gradient control | ||
``` js | ||
@@ -72,14 +26,4 @@ import React, { Component } from 'react'; | ||
super(); | ||
this.onClick = this.onClick.bind(this); | ||
this.state = ({ | ||
value: (Math.random() * 100), | ||
}); | ||
} | ||
onClick() { | ||
this.setState({ | ||
value: (Math.random() * 100), | ||
}); | ||
} | ||
render() { | ||
@@ -94,12 +38,20 @@ return ( | ||
<LiquidChart | ||
responsive | ||
gradient="liquid" | ||
animate | ||
ease='easeBackIn' | ||
animateWaves | ||
animationWavesTime={4000} | ||
onClick={this.onClick} | ||
amplitude={4} | ||
value={this.state.value} | ||
/> | ||
responsive | ||
legend="Percentage of Completed Tasks" | ||
value={Math.random() * 100} | ||
showDecimal | ||
amplitude={4} | ||
frequency={2} | ||
animationTime={2000} | ||
animationWavesTime={2250} | ||
gradient={{ | ||
type: 1, | ||
x1: 0, | ||
x2: 0, | ||
y1: 100, | ||
y2: 0, | ||
}} | ||
postfix="%" | ||
legendFontSize={0.1} | ||
/> | ||
</div> | ||
@@ -112,79 +64,26 @@ ); | ||
## Components | ||
This chart is broken down into components. | ||
### Chart | ||
This is the container that renders the SVG element and the children. | ||
It can keep the chart responsive so you dont have to worry about the width and heigth if you have a responsive layout. | ||
### Chart PropTypes | ||
### LiquidChart PropTypes | ||
Name|Type|Default|Description| | ||
---|---|---|--- | ||
responsive|boolean|*true*| Rerenders the chart on screen resize | ||
width|number|*none*| sets the width of the component, if responsive is true then it will take fill out into the parent container | ||
height|number|*none*| sets the height of the component, the same applies here to the responsive prop. | ||
gradient|number|*null*| sets a gradient on the chart 1 is for linear 2 is for radial, you can plug in stop children in the Gradient component if you want to customize the colors, by default this takes the liquid fill and creates a small color scale around that. | ||
### Liquid | ||
This is the main element that renders all the visable elements in the chart along with one defs for the clipPath. | ||
### Liquid PropTypes | ||
Name|Type|Default|Description| | ||
---|---|---|--- | ||
Value|number|*65*|The value | ||
animate|bool|*false*|If true then the chart is animated | ||
animateWaves|bool|*false*|if true then the waves will loop between the liquid radius and 0 forever. | ||
animationWavesTime|number|*2000*|The speed of the wave animation | ||
animationTime|number|2000| milliseconds for animation length | ||
ease|string|*'easeCubicInOut'*|The name of the d3 easing function, other values like, easeBack, easeBackInOut, easeSinInOut, easeExpInOut. See d3 easing page for more ideas. | ||
outerRadius|number|*0.9*|This is the outerRadius of the chart where 1 would be 100% of the radius and 0 would be 0% of the radius.. | ||
innerRadius|number|*0.8*|The innerwidth of the outerpath surronding the liquid, again 0.8 would be 80% of the radius. | ||
Width|number|*null*| The width of the chart, used if responsive is false | ||
height|number|*null*|The width of the chart, used if responsive is false | ||
responsive|bool|*true*|If set to true then the element will fill out into parent container, and resize on window dimension change | ||
Value|number|*65*|The value, tops at 100 and 0 is the minimum | ||
animationWavesTime|number|*null*|The speed of the wave animation, going back and forth | ||
animationTime|number|*2000*| milliseconds for animation when updating value | ||
animationEase|string|*'easeCubicInOut'*|The name of the d3 easing function, other values like, easeBack, easeBackInOut, easeSinInOut, easeExpInOut. See d3 easing page for more ideas. | ||
outerBound|number|*0.9*|This is the outerRadius of the chart where 1 would be 100% of the radius and 0 would be 0% of the radius.. | ||
innerBound|number|*0.8*|The innerwidth of the outerpath surronding the liquid, again 0.8 would be 80% of the radius. | ||
margin|number|*0.025*|The margin between the outer path and the liquid, here 0.025 would be 2.5% | ||
amplitude|number|2|The Amplitude X * sine(frequency) part of the formula | ||
frequency|number|4|Still have to fix this one, this is actually the inverse of frequency it's sine(x/freq) so the higher the number the smoother the wave. | ||
waveScaleLimit|bool|true|This is in the original, this will create a scale that limits the wave height close to 0 or 100 | ||
outerArcStyle|shape|{ fill: 'rgb(23,139,202)'}| The style of the outerarc fill and stroke | ||
liquid|shape|{ fill: 'rgb(23, 139, 202)'}| The style of the liquid, fill and stroke | ||
liquidNumber|shape|{fill: 'rgb(164, 219, 248)'}| The style of the number that is in the liquid, fill and stroke | ||
number|shape|{fill: 'rgb(4, 86, 129)'}| The style of the number that is not in the liquid, fill and stroke | ||
amplitude|number|*4*|The Amplitude X * sine(frequency) part of the formula | ||
frequency|number|*2*|The frequency, how many full circles are in the chart. | ||
waveScaleLimit|bool|*true*|This is in the original, this will create a scale that limits the wave height close to 0 or 100 | ||
outerStyle|shape|*{ fill: 'rgb(23,139,202)'}*| The style of the outerarc fill and stroke | ||
liquidStyle|shape|*{ fill: 'rgb(23, 139, 202)'}*| The style of the liquid, fill and stroke | ||
dryStyle|shape|*{fill: 'rgb(164, 219, 248)'}*| The style of the number that is in the liquid, fill and stroke | ||
wetStyle|shape|*{fill: 'rgb(4, 86, 129)'}*| The style of the number that is not in the liquid, fill and stroke | ||
fontSizes|shape|| The sizes of the fonts in ratio to the radius. | ||
### Gradient | ||
This component is nothing more than an ultra thin wrapper around a linearGradient defs. | ||
It takes in stop to create the gradient effect. | ||
But by default it uses shades of the liquid.fill color to create a simple gradient. | ||
### Gradient PropTypes | ||
Name|Type|Default|Description| | ||
---|---|---|--- | ||
x1|number|*0*|The X start coordinate | ||
x2|number|*0*|The X end coordinate | ||
y1|number|*100*|The Y start coordinate, remember it starts on the bottom | ||
y2|number|*0*|The Y end coordinate | ||
#### Example | ||
```js | ||
// Create a Gradient effect going from the bottom left to the top right part of the liquid area | ||
<Gradient | ||
x1={0} | ||
y1={100} | ||
x2={100} | ||
y2={0} | ||
> | ||
<stop stopColor={someColor1} offset="0%" /> | ||
<stop stopColor={someColor2} offset="25%" /> | ||
<stop stopColor={someColor3} offset="50%" /> | ||
<stop stopColor={someColor4} offset="75%" /> | ||
<stop stopColor={someColor5} offset="100%" /> | ||
</Gradient> | ||
``` | ||
### LiquidChart PropTypes | ||
All of the above propTypes apply, except you cant insert the stop children into the gradient so it just uses | ||
the liquid.fill color to create the gradient. | ||
### TODO | ||
1. Write some tests | ||
2. Create a Text Component | ||
#### Licence | ||
This software was available initially under the BSD-2-Clause and it still is. | ||
Please see the original and put a github like on it at https://gist.github.com/brattonc/5e5ce9beee483220e2f6 |
import React, { Component, PropTypes } from 'react'; | ||
import { timer } from 'd3-timer'; | ||
import { arc, area } from 'd3-shape'; | ||
import * as ease from 'd3-ease'; | ||
import cloneChildren from 'react-offcharts-core/Utils/cloneChildren'; | ||
import arcDim from 'react-offcharts-core/Helpers/arcDimension'; | ||
import { round, splitNumber } from 'react-offcharts-core/Utils/numbers'; | ||
import { select, selectAll } from 'd3-selection'; | ||
import { scaleLinear } from 'd3-scale'; | ||
import * as ease from 'd3-ease'; | ||
import 'd3-transition'; | ||
import { interpolate } from 'd3-interpolate'; | ||
import * as d3Color from 'd3-color'; | ||
import 'd3-transition'; | ||
import ReactIf from './ReactIf'; | ||
import Text from './Text'; | ||
import * as ch from '../Helpers/constants'; | ||
import * as dh from '../Helpers/dimensions'; | ||
/* | ||
PropType for fill and stroke.. | ||
*/ | ||
const fillStroke = PropTypes.shape({ | ||
fill: PropTypes.string, | ||
stroke: PropTypes.string, | ||
}); | ||
const sample = 60; | ||
export default class LiquidChart extends Component { | ||
export default class Liquid extends Component { | ||
static propTypes = { | ||
// a percentage from 0 to 100 | ||
value: PropTypes.number, | ||
// boolean if true then animate | ||
animate: PropTypes.bool, | ||
// comes from Chart parent | ||
width: PropTypes.number, | ||
// comes from Chart parent | ||
height: PropTypes.number, | ||
// innerRadius | ||
innerRadius: PropTypes.number, | ||
// outer radius | ||
outerRadius: PropTypes.number, | ||
// margin between inner liquid and innerRadius | ||
margin: PropTypes.number, | ||
// callback function called when animation is done | ||
onEnd: PropTypes.func, | ||
// string | ||
ease: PropTypes.string, | ||
// animation Time | ||
value: PropTypes.oneOfType([ | ||
PropTypes.string, | ||
PropTypes.number, | ||
]), | ||
outerBound: PropTypes.number, | ||
innerBound: PropTypes.number, | ||
animationEase: PropTypes.string, | ||
animationTime: PropTypes.number, | ||
// animation wave time | ||
animationWavesTime: PropTypes.number, | ||
// The fill and stroke for the outer arc | ||
outerArcStyle: fillStroke, | ||
// The fill and stroke for the liquid | ||
liquid: fillStroke, | ||
// The fill and stroke for the number part that | ||
// is drenched in liquid | ||
liquidNumber: fillStroke, | ||
// the fill and stroke of the number that is not drenched in liquid | ||
number: fillStroke, | ||
// the wave amplitude | ||
amplitude: PropTypes.number, | ||
// the wave frequncy inverse, the higer the number the fewer the waves | ||
frequency: PropTypes.number, | ||
// scaling the wave when it´s close to 0 or 100 | ||
waveScaleLimit: PropTypes.bool, | ||
// on click | ||
onClick: PropTypes.func, | ||
// if true then animate waves | ||
animateWaves: PropTypes.bool, | ||
// 1 for linear 2 for radial | ||
gradient: PropTypes.number, | ||
} | ||
static defaultProps = { | ||
value: 65, | ||
animate: false, | ||
outerRadius: 0.9, | ||
innerRadius: 0.8, | ||
margin: 0.025, | ||
ease: 'easeCubicInOut', | ||
animationTime: 2000, | ||
animationWavesTime: 2000, | ||
amplitude: 2, | ||
amplitude: 1, | ||
liquidMargin: 0.005, | ||
waveScaleLimit: true, | ||
frequency: 4, | ||
outerArcStyle: { | ||
fill: 'rgb(23, 139, 202)', | ||
}, | ||
liquid: { | ||
fill: 'rgb(23, 139, 202)', | ||
}, | ||
liquidNumber: { | ||
fill: 'rgb(164, 219, 248)', | ||
}, | ||
number: { | ||
fill: 'rgb(4, 86, 129)', | ||
}, | ||
offsetX: 1, | ||
offsetY: 1, | ||
onClick: () => {}, | ||
fontSize: '7rem', | ||
smallFontSize: '3rem', | ||
}; | ||
deliminator: '.', | ||
postfix: '', | ||
} | ||
constructor(props) { | ||
super(); | ||
if (props.innerBound > props.outerBound) { | ||
console.warn(ch.INNER_BIGGER_THAN_OUTER); | ||
} else if (props.outerBound > 1) { | ||
console.warn(ch.OUTER_BIGGER_THAN_ONE); | ||
} | ||
this.iter = 0; | ||
} | ||
componentDidMount() { | ||
if (this.props.animate) { | ||
this.animate(); | ||
return; | ||
} | ||
this.draw(); | ||
this.renderChart(); | ||
} | ||
componentDidUpdate(prevProps, prevState) { | ||
if (this.props.animate) { | ||
this.animate(); | ||
return; | ||
this.renderChart(); | ||
} | ||
getEasing() { | ||
const animationEase = ease[this.props.animationEase]; | ||
if (typeof animationEase === 'function') { | ||
return animationEase; | ||
} | ||
this.draw(); | ||
return ease.easeCubicInOut; | ||
} | ||
setRes() { | ||
// set an array filled with zeros, | ||
// we are just going to use the index anyways | ||
this.arr = new Array(100); | ||
// get the clip path | ||
this.wave = select(this.clipPath).datum([this.props.value]); | ||
// get the tspan | ||
this.text = select(this.container) | ||
.selectAll('text') | ||
.selectAll('tspan.value'); | ||
getAnimationTime() { | ||
const animationTime = this.props.animationTime; | ||
const height = (this.props.height * (this.props.innerRadius - this.props.margin)) / 2; | ||
// to animate the wave we need to set the width of the path double the diameter | ||
// of the liquid. | ||
this.x = scaleLinear().range([-this.liquidRadius * 2, this.liquidRadius * 2]).domain([0, 100]); | ||
this.y = scaleLinear().range([height, -height]).domain([0, 100]); | ||
if (animationTime === undefined) { | ||
return 2000; | ||
} | ||
return animationTime; | ||
} | ||
draw() { | ||
// ready the chart | ||
this.setRes(); | ||
// some basic trig | ||
const val = area() | ||
.x((d, i) => this.x(i)) | ||
.y0((d, i) => this.y( | ||
(this.props.amplitude * Math.sin(i / this.props.frequency)) + | ||
this.props.value)) | ||
.y1(d => this.props.height / 2); | ||
// set the clip path d attribute | ||
this.wave.attr('d', val(this.arr)); | ||
// set the text to the rounded value | ||
// decimal formatting todo | ||
this.text.text(Math.round(this.props.value)); | ||
animateBackAndForth() { | ||
// Set the sampling array to a new array of X times undefines | ||
// does not matter because we only use zeros | ||
const arr = new Array(ch.SAMPLING); | ||
// Get the container element | ||
const container = select(this.container); | ||
// select the clippath that is going to be animated | ||
const wave = container.select('clipPath').select('path'); | ||
// Get the easing type, if the user misspelled the easing or | ||
const animationEase = this.getEasing(); | ||
// Get the animationtime | ||
const animationTime = this.getAnimationTime(); | ||
// get the wavescale | ||
const waveScale = dh.getWaveScaleLimit(this.props); | ||
// get the scales and the area function | ||
const { waveArea, x, y } = dh.getWaveArea(this.props); // { waveArea, x, y, w, h } | ||
const { forthAmplitude, backAmplitude, forthFrequency, backFrequency } = dh.getBackAndForth(); | ||
const textValue = container.selectAll(`.${ch.TEXT_VALUE}`); | ||
const textDecimal = container.selectAll(`.${ch.TEXT_DECIMAL}`); | ||
const animation = () => { | ||
wave | ||
.transition() | ||
.duration(this.props.animationWavesTime) | ||
.ease(ease.easeSinInOut) | ||
.attrTween('d', () => { | ||
wave.node().M = 1; | ||
return (t1) => { | ||
const amp = forthAmplitude(t1); | ||
const freq = forthFrequency(t1); | ||
const ws = amp * waveScale(this.props.value); | ||
waveArea | ||
.y0((d, i) => ( | ||
y(dh.sine(ws, i, this.props.frequency, freq) + this.props.value) | ||
)); | ||
wave.node().A = amp; | ||
wave.node().F = freq; | ||
return waveArea(arr); | ||
}; | ||
}) | ||
.transition() | ||
.duration(this.props.animationWavesTime) | ||
.ease(ease.easeSinInOut) | ||
.attrTween('d', () => { | ||
wave.node().M = 0; | ||
return (t1) => { | ||
const amp = backAmplitude(t1); | ||
const freq = backFrequency(t1); | ||
const ws = amp * waveScale(this.props.value); | ||
waveArea | ||
.y0((d, i) => ( | ||
y(dh.sine(ws, i, this.props.frequency, freq) + this.props.value) | ||
)); | ||
wave.node().A = amp; | ||
wave.node().F = freq; | ||
return waveArea(arr); | ||
}; | ||
}) | ||
.on('end', () => { | ||
animation(); | ||
}); | ||
}; | ||
wave | ||
.transition() | ||
.duration(animationTime) | ||
.ease(animationEase) | ||
.attrTween('d', () => { | ||
const interVal = interpolate(wave.node().old || 0, this.props.value); | ||
const { | ||
amplitudeScale, | ||
frequencyScale, | ||
} = dh.getWaveValueMovement(wave.node()); | ||
const updateNum = (val) => { | ||
const value = round(val); | ||
const sp = splitNumber(value, this.props.deliminator); | ||
textValue.text(sp.number); | ||
textDecimal.text(`.${sp.fraction}`); | ||
}; | ||
return (t) => { | ||
const val = interVal(t); | ||
const amp = amplitudeScale(t); | ||
const freq = frequencyScale(t); | ||
const ws = amp * waveScale(val); | ||
waveArea | ||
.y0((d, i) => ( | ||
y(dh.sine(ws, i, this.props.frequency, freq) + val) | ||
)); | ||
wave.node().old = val; | ||
wave.node().A = amp; | ||
wave.node().F = freq; | ||
updateNum(val); | ||
return waveArea(arr); | ||
}; | ||
}) | ||
.on('end', () => { | ||
animation(); | ||
}); | ||
} | ||
animate() { | ||
// set the ease | ||
const easeFn = ease[this.props.ease] ? ease[this.props.ease] : ease.easeCubicInOut; | ||
// ready the chart and do calculations | ||
this.setRes(); | ||
const val = area() | ||
.x((d, i) => this.x(i)) | ||
.y1(d => this.props.height / 2); | ||
// reduce the wave when it's close to 0 or 100 | ||
let waveScale; | ||
if (this.props.waveScaleLimit) { | ||
waveScale = scaleLinear() | ||
.range([0, this.props.amplitude, 0]) | ||
.domain([0, 50, 100]); | ||
} else { | ||
waveScale = scaleLinear() | ||
.range([this.props.amplitude, this.props.amplitude]) | ||
.domain([0, 50, 100]); | ||
} | ||
animateValue() { | ||
// Set the sampling array to a new array of X times undefines | ||
// does not matter because we only use zeros | ||
const arr = new Array(ch.SAMPLING); | ||
if (this.props.animateWaves) { | ||
this.animateWave(); | ||
} | ||
// the d3 timer goes from from 0 to 1 | ||
const time = scaleLinear().range([0, 1]).domain([0, this.props.animationTime]); | ||
// if the wave does not have old value then interpolate from 0 to value else old to value | ||
const interpolateValue = interpolate(this.wave.node().old || 0, this.props.value); | ||
// Get the container element | ||
const container = select(this.container); | ||
// start animation | ||
const animationTimer = timer((t) => { | ||
// set the easing | ||
const animate = easeFn(time(t)); | ||
const value = interpolateValue(animate); | ||
// calculate the wave | ||
val.y0((d, i) => this.y( | ||
(waveScale(value) * Math.sin(i / this.props.frequency)) | ||
+ value)); | ||
// set the text value | ||
this.text.text(Math.round(value)); | ||
// set the wave data attribute | ||
this.wave.attr('d', val(this.arr)); | ||
// the transition has ended | ||
if (t >= this.props.animationTime) { | ||
// stop the timer | ||
animationTimer.stop(); | ||
// Make sure that the animation stops in the right place | ||
// we set 1 as the interpolation parameter | ||
val.y0((d, i) => this.y( | ||
(waveScale(this.props.value) * Math.sin(i / this.props.frequency)) | ||
+ interpolateValue(1))); | ||
this.text.text(Math.round(interpolateValue(1))); | ||
this.wave.attr('d', val(this.arr)); | ||
// if the onEnd prop is set then call the function | ||
if (this.props.onEnd !== undefined) { | ||
this.props.onEnd(); | ||
} | ||
} | ||
}); | ||
// Store the old node value so that we can animate from | ||
// that point again | ||
this.wave.node().old = this.props.value; | ||
// select the clippath that is going to be animated | ||
const wave = container.select('clipPath').select('path'); | ||
// Get the easing type, if the user misspelled the easing or | ||
const animationEase = this.getEasing(); | ||
// Get the animationtime | ||
const animationTime = this.getAnimationTime(); | ||
// get the wavescale | ||
const waveScale = dh.getWaveScaleLimit(this.props); | ||
// get the areafunction and dimensions | ||
const { waveArea, x, y } = dh.getWaveArea(this.props); // { waveArea, x, y, w, h } | ||
// get the text variables | ||
const textValue = container.selectAll(`.${ch.TEXT_VALUE}`); | ||
const textDecimal = container.selectAll(`.${ch.TEXT_DECIMAL}`); | ||
wave | ||
.transition() | ||
.duration(animationTime) | ||
.ease(animationEase) | ||
.attrTween('d', () => { | ||
const interVal = interpolate(wave.node().old || 0, this.props.value); | ||
const updateNum = (val) => { | ||
const value = round(val); | ||
const sp = splitNumber(value, '.'); | ||
textValue.text(sp.number); | ||
textDecimal.text(`.${sp.fraction}`); | ||
}; | ||
return (t) => { | ||
const val = interVal(t); | ||
const ws = waveScale(val); | ||
waveArea | ||
.y0((d, i) => ( | ||
y(dh.sine(ws, i, this.props.frequency, 0) + val) | ||
)); | ||
wave.node().old = val; | ||
updateNum(val); | ||
return waveArea(arr); | ||
}; | ||
}); | ||
} | ||
// animate the wave from 0 to liquidRadius repeat | ||
// not perfect but works | ||
animateWave() { | ||
// put a lock on animate function | ||
// so it's not called often | ||
if (!this.isOn) { | ||
this.isOn = true; | ||
const anime = () => { | ||
this.wave | ||
.attr('transform', 'translate(0,0)') | ||
.transition() | ||
.duration(this.props.animationWavesTime) | ||
.ease(ease.easeLinear) | ||
.attrTween('transform', () => { | ||
const i = interpolate(0, this.liquidRadius); | ||
return (t) => { | ||
const yex = 3; | ||
return `translate(${i(t)},0)`; | ||
}; | ||
}) | ||
.on('end', () => { | ||
anime(); | ||
}); | ||
}; | ||
anime(); | ||
animate() { | ||
if (this.props.animationWavesTime) { | ||
this.animateBackAndForth(); | ||
return; | ||
} | ||
this.animateValue(); | ||
} | ||
render() { | ||
this.radius = Math.min(this.props.height / 2, this.props.width / 2); | ||
this.liquidRadius = this.radius * (this.props.innerRadius - this.props.margin); | ||
// set the outerArc arc parameters | ||
const outerArc = arc() | ||
.outerRadius(this.props.outerRadius * this.radius) | ||
.innerRadius(this.props.innerRadius * this.radius) | ||
.startAngle(0) | ||
.endAngle(Math.PI * 2); | ||
// set the chart center | ||
const cX = this.props.width / 2; | ||
const cY = this.props.height / 2; | ||
draw() { | ||
const arr = new Array(ch.SAMPLING); | ||
const container = select(this.container); | ||
const el = container.select('clipPath').select('path'); | ||
const textValue = container.selectAll(`.${ch.TEXT_VALUE}`); | ||
const decimalValue = container.selectAll(`.${ch.TEXT_DECIMAL}`); | ||
decimalValue.text('.3'); | ||
textValue.text(parseInt(this.props.value, 10)); | ||
el.attr('d', dh.getWave(this.props)(arr)); | ||
} | ||
let fillCircle = this.props.liquid.fill; | ||
renderChart() { | ||
const shouldAnimate = ( | ||
this.props.animationTime || this.props.animationEase | ||
); | ||
if (this.props.gradient) { | ||
fillCircle = 'url(#gradient)'; | ||
if (shouldAnimate) { | ||
this.animate(); | ||
return; | ||
} | ||
this.draw(); | ||
} | ||
render() { | ||
const d = dh.getDimensions(this.props); | ||
return ( | ||
<g | ||
transform={`translate(${cX},${cY})`} | ||
ref={(c) => { this.container = c; }} | ||
transform={`translate(${d.cx},${d.cy})`} | ||
> | ||
<defs> | ||
<clipPath id="clip"> | ||
<path ref={(c) => { this.clipPath = c; }} /> | ||
</clipPath> | ||
</defs> | ||
{Text(this.props, this.props.number)} | ||
<g clipPath="url(#clip)"> | ||
<circle | ||
r={this.liquidRadius} | ||
fill={fillCircle} | ||
/> | ||
{Text(this.props, this.props.liquidNumber)} | ||
</g> | ||
<path | ||
d={outerArc()} | ||
fill={this.props.outerArcStyle.fill} | ||
stroke={this.props.outerArcStyle.stroke} | ||
/> | ||
<circle | ||
r={this.radius} | ||
fill="rgba(0,0,0,0)" | ||
stroke="rgba(0,0,0,0)" | ||
style={{ pointerEvents: 'all' }} | ||
onClick={() => { this.props.onClick(); }} | ||
/> | ||
{cloneChildren(this.props, d)} | ||
</g> | ||
@@ -289,0 +274,0 @@ ); |
import React, { PropTypes } from 'react'; | ||
import Chart from './Chart'; | ||
import Chart from 'react-offcharts-core/Components/Chart'; | ||
import ReactIf from 'react-offcharts-core/Components/ReactIf'; | ||
import Gradients from 'react-offcharts-core/Components/Defs/Gradients'; | ||
import guid from 'react-offcharts-core/Utils/guid'; | ||
import Clip from './Clip'; | ||
import Text from './Text'; | ||
import Shape from './Shape'; | ||
import Liquid from './Liquid'; | ||
import Gradient from './Gradient'; | ||
import { dShape, fillAndStroke, gradientProps } from '../Helpers/props'; | ||
const gradientId = guid(); | ||
const LiquidChart = props => ( | ||
<Chart | ||
childRules={false} | ||
{...props} | ||
width={props.width} | ||
height={props.height} | ||
responsive={props.responsive} | ||
value={props.value} | ||
clipId={guid()} | ||
gradientId={gradientId} | ||
> | ||
<Liquid /> | ||
<Gradient /> | ||
<Liquid | ||
outerBound={0.95} | ||
innerBound={0.85} | ||
amplitude={props.amplitude} | ||
frequency={props.frequency} | ||
waveScaleLimit={props.waveScaleLimit} | ||
animationWavesTime={props.animationWavesTime} | ||
animationTime={props.animationTime} | ||
animationEase={props.animationEase} | ||
> | ||
<Shape | ||
type={props.type} | ||
outerStyle={props.outerStyle} | ||
liquidStyle={props.liquidStyle} | ||
wetStyle={props.wetStyle} | ||
dryStyle={props.dryStyle} | ||
> | ||
<Text | ||
deliminator={props.deliminator} | ||
postfix={props.postfix} | ||
showDecimal={props.showDecimal} | ||
fontSizes={props.fontSizes} | ||
legend={props.legend} | ||
/> | ||
</Shape> | ||
<ReactIf condition={props.gradient.type} el={<g />}> | ||
<Gradients | ||
id={gradientId} | ||
{...props.gradient} | ||
fill={props.liquidStyle.fill} | ||
/> | ||
</ReactIf> | ||
<Clip /> | ||
</Liquid> | ||
</Chart> | ||
); | ||
LiquidChart.propTypes = { | ||
width: PropTypes.number, | ||
height: PropTypes.number, | ||
responsive: PropTypes.bool, | ||
value: PropTypes.number, | ||
amplitude: PropTypes.number, | ||
frequency: PropTypes.number, | ||
waveScaleLimit: PropTypes.bool, | ||
animationWavesTime: PropTypes.number, | ||
animationEase: PropTypes.string, | ||
animationTime: PropTypes.number, | ||
type: PropTypes.number, | ||
outerStyle: fillAndStroke, | ||
liquidStyle: fillAndStroke, | ||
wetStyle: fillAndStroke, | ||
dryStyle: fillAndStroke, | ||
deliminator: PropTypes.string, | ||
postfix: PropTypes.string, | ||
legend: PropTypes.string, | ||
showDecimal: PropTypes.bool, | ||
fontSizes: PropTypes.shape({ | ||
value: PropTypes.number, | ||
decimal: PropTypes.number, | ||
postfix: PropTypes.number, | ||
legend: PropTypes.number, | ||
}), | ||
gradient: PropTypes.shape({ | ||
type: PropTypes.number, | ||
r: PropTypes.number, | ||
x: PropTypes.number, | ||
x1: PropTypes.number, | ||
x2: PropTypes.number, | ||
y: PropTypes.number, | ||
y1: PropTypes.number, | ||
y2: PropTypes.number, | ||
cx: PropTypes.number, | ||
cy: PropTypes.number, | ||
fx: PropTypes.number, | ||
fy: PropTypes.number, | ||
stops: PropTypes.arrayOf(PropTypes.node), | ||
}), | ||
}; | ||
LiquidChart.defaultProps = { | ||
outerBound: 0.9, | ||
innerBound: 0.85, | ||
margin: 0.005, | ||
frequency: 2, | ||
amplitude: 4, | ||
waveScaleLimit: true, | ||
outerStyle: { fill: 'rgb(23,139,202)' }, | ||
liquidStyle: { fill: 'rgb(23, 139, 202)' }, | ||
dryStyle: { fill: 'rgb(4, 86, 129)' }, | ||
wetStyle: { fill: 'rgb(164, 219, 248)' }, | ||
gradient: { | ||
x1: 0, | ||
x2: 0, | ||
y1: 100, | ||
y2: 0, | ||
}, | ||
}; | ||
export default LiquidChart; | ||
@@ -1,25 +0,62 @@ | ||
import React, { Component, PropTypes } from 'react'; | ||
import React, { PropTypes } from 'react'; | ||
import * as ch from '../Helpers/constants'; | ||
const Text = (props, { fill, stroke }) => ( | ||
<text | ||
textAnchor="middle" | ||
fontSize={props.fontSize} | ||
fill={fill} | ||
stroke={stroke} | ||
dy={props.dy} | ||
dx={props.dx} | ||
> | ||
<tspan className="value">{props.value}</tspan> | ||
<tspan fontSize={props.smallFontSize}>%</tspan> | ||
</text> | ||
const LiquidText = props => ( | ||
<g> | ||
<text {...props.style} textAnchor="middle"> | ||
<tspan | ||
className={ch.TEXT_VALUE} | ||
fontSize={props.fontSizes.value * props.radius} | ||
/> | ||
{ props.showDecimal ? | ||
<tspan | ||
className={ch.TEXT_DECIMAL} | ||
dx={(props.fontSizes.decimal * props.radius) * -0.01} | ||
fontSize={props.fontSizes.decimal * props.radius} | ||
/> : null | ||
} | ||
<tspan | ||
className={ch.TEXT_POSTFIX} | ||
fontSize={props.fontSizes.postfix * props.radius} | ||
>{props.postfix}</tspan> | ||
</text> | ||
<text | ||
{...props.style} | ||
className={ch.TEXT_LEGEND} | ||
dy={props.radius * (props.fontSizes.legend + 0.05)} | ||
fontSize={props.radius * props.fontSizes.legend} | ||
textAnchor="middle" | ||
>{props.legend}</text> | ||
</g> | ||
); | ||
Text.propTypes = { | ||
value: PropTypes.number, | ||
smallFontSize: PropTypes.string, | ||
fontSize: PropTypes.string, | ||
dy: PropTypes.string, | ||
dx: PropTypes.string, | ||
const dShape = PropTypes.shape({ | ||
dx: PropTypes.number, | ||
dy: PropTypes.number, | ||
}); | ||
LiquidText.propTypes = { | ||
fontSizes: PropTypes.shape({ | ||
value: PropTypes.number, | ||
decimal: PropTypes.number, | ||
postfix: PropTypes.number, | ||
legend: PropTypes.number, | ||
}), | ||
radius: PropTypes.number, | ||
postfix: PropTypes.string, | ||
legend: PropTypes.string, | ||
style: PropTypes.shape({}), | ||
showDecimal: PropTypes.bool, | ||
}; | ||
export default Text; | ||
LiquidText.defaultProps = { | ||
fontSizes: { | ||
value: 0.5, | ||
decimal: 0.35, | ||
postfix: 0.25, | ||
legend: 0.1, | ||
}, | ||
}; | ||
export default LiquidText; |
@@ -1,8 +0,9 @@ | ||
import Chart from './Chart/Chart'; | ||
import Shape from './Chart/Shape'; | ||
import Liquid from './Chart/Liquid'; | ||
import Gradient from './Chart/Gradient'; | ||
import Clip from './Chart/Clip'; | ||
import Text from './Chart/Text'; | ||
import LiquidChart from './Chart/LiquidChart'; | ||
export { Chart, Liquid, Gradient }; | ||
export { Shape, Clip, Liquid, Text }; | ||
export default LiquidChart; |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
74680
10
36
1632
22
86
1
+ Addedreact-offcharts-core@^0.1.2
+ Addedreact-offcharts-core@0.1.71(transitive)
+ Addedresize-observer-polyfill@1.5.1(transitive)
- Removedd3-timer@^1.0.3
- Removedlodash.throttle@^4.1.1