Comparing version 0.5.19 to 0.5.20
@@ -35,2 +35,4 @@ 'use strict'; | ||
var _d3Array = require('d3-array'); | ||
var _d3Selection = require('d3-selection'); | ||
@@ -56,3 +58,6 @@ | ||
var LEGEND_HEIGHT = 75; | ||
var ASPECT_THRESHOLD = 1.3; | ||
var sizingProps = ['areaHeight', 'areaWidth', 'legendHeight', 'textKey', 'valueKey', 'originalData']; | ||
var CircleDistribution = function (_Component) { | ||
@@ -74,3 +79,5 @@ (0, _inherits3.default)(CircleDistribution, _Component); | ||
_this.setState({ hoveredItem: data }, function () { | ||
_this.tooltipRenderer.update(_react2.default.Children.only(_this.props.children), { anchor: _TooltipAnchors2.default.path }); | ||
if (_this.hasTooltip()) { | ||
_this.tooltipRenderer.update(_react2.default.Children.only(_this.props.children), { anchor: _TooltipAnchors2.default.path }); | ||
} | ||
_this.renderHighlightState(); | ||
@@ -116,3 +123,3 @@ }); | ||
return _react2.default.createElement( | ||
'div', | ||
'li', | ||
{ | ||
@@ -138,9 +145,24 @@ key: item[textKey], | ||
_this.renderViz = function () { | ||
var sizeScale = _this.scaleForSize(_this.data); | ||
_this.circleData = _this.circleData || _this.prepareCircleData(sizeScale); | ||
if (!_this.data.length) { | ||
return; | ||
} | ||
var valueKey = _this.getValueKey(); | ||
var sortedData = (0, _lodash.cloneDeep)(_this.data); | ||
sortedData.sort(function (a, b) { | ||
return b[valueKey] - a[valueKey]; | ||
}); | ||
var sizeScale = _this.scaleForSize(sortedData); | ||
_this.circleData = _this.prepareCircleData(sizeScale, sortedData); | ||
var maxRadius = sizeScale.range()[1]; | ||
var centers = [_this.props.areaWidth / 2, _this.props.areaWidth * 2 / 3 + maxRadius, _this.props.areaWidth / 6]; | ||
var centers = _this.setupCenters(maxRadius); | ||
var parentNode = (0, _d3Selection.select)(_this.node); | ||
var circles = parentNode.selectAll('circle').data(_this.circleData); | ||
var simulation = (0, _d3Force.forceSimulation)().nodes(_this.circleData).force('x', (0, _d3Force.forceX)().x(function (d) { | ||
circles.exit().remove(); | ||
var text = parentNode.selectAll('text').data(_this.circleData); | ||
text.exit().remove(); | ||
_this.simulation = (0, _d3Force.forceSimulation)().nodes(_this.circleData).force('x', (0, _d3Force.forceX)().x(function (d) { | ||
return centers[d.center]; | ||
@@ -151,5 +173,6 @@ }).strength(1)).force('collision', (0, _d3Force.forceCollide)().radius(function (d) { | ||
(0, _lodash.times)(30, function () { | ||
return simulation.tick(); | ||
return _this.simulation.tick(); | ||
}); | ||
simulation.on('tick', _this.onForceTick.bind(_this, _this.circleData)).on('end', _this.onSimulationEnd.bind(_this, _this.circleData)); | ||
_this.simulation.on('tick', _this.onForceTick.bind(_this, circles)).on('end', _this.onSimulationEnd.bind(_this, text)); | ||
_this.simulation.restart(); | ||
}; | ||
@@ -176,4 +199,4 @@ | ||
var highlight = svg.selectAll('.circle-highlight').data([item]); | ||
highlight.enter().append('circle').attr('class', 'circle-highlight').style('fill', 'none').style('stroke', 'black').attr('r', function (d) { | ||
return d.r + 3; | ||
highlight.enter().append('circle').attr('class', 'circle-highlight').attr('stroke', 'black').attr('stroke-width', _this.props.circleHighlightWidth || 1).style('fill', 'none').attr('r', function (d) { | ||
return d.r + (_this.props.circleHighlightWidth ? _this.props.circleHighlightWidth * 2 : 3); | ||
}).attr('cx', function (d) { | ||
@@ -227,15 +250,31 @@ return d.x; | ||
}, { | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(nextProps) { | ||
var currentProps = (0, _lodash.pick)(this.props, sizingProps); | ||
var newProps = (0, _lodash.pick)(nextProps, sizingProps); | ||
if (!(0, _lodash.isEqual)(currentProps, newProps)) { | ||
var state = { renderComplete: false }; | ||
if (this.dataChanged(nextProps)) { | ||
state.dataChanged = true; | ||
} | ||
this.setState(state); | ||
} | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate() { | ||
if (this.data.length !== this.props.originalData.length) { | ||
this.data = (0, _lodash.cloneDeep)(this.props.originalData); | ||
} | ||
value: function componentDidUpdate(prevProps, prevState) { | ||
if (this.hasTooltip()) { | ||
this.tooltipRenderer.update(_react2.default.Children.only(this.props.children), { anchor: _TooltipAnchors2.default.mouse }); | ||
} | ||
if (prevState.hoveredItem !== this.state.hoveredItem) { | ||
return; | ||
} | ||
if (this.state.renderComplete) { | ||
if (this.state.dataChanged) { | ||
this.data = (0, _lodash.cloneDeep)(this.props.originalData); | ||
} | ||
if (!this.state.renderComplete && !this.simulation) { | ||
this.renderViz(); | ||
} else if (this.state.renderComplete) { | ||
this.simulation = null; | ||
this.attachHandlers(); | ||
@@ -245,13 +284,19 @@ } | ||
}, { | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
if (this.simulation) { | ||
this.simulation = null; | ||
} | ||
this.tooltipRenderer.onHide.call(this.node); | ||
} | ||
}, { | ||
key: 'onForceTick', | ||
value: function onForceTick(circleData) { | ||
var svg = (0, _d3Selection.select)(this.node); | ||
var circles = svg.selectAll('circle').data(circleData); | ||
circles.enter().append('circle').attr('fill', this.setFill).attr('r', function (c) { | ||
return c.r; | ||
}).attr('fill-opacity', 0).attr('cx', function (c) { | ||
value: function onForceTick(circleSelection) { | ||
circleSelection.enter().append('circle').attr('fill', this.setFill).attr('fill-opacity', 0).attr('cx', function (c) { | ||
return c.x || 0; | ||
}).attr('cy', function (c) { | ||
return c.y; | ||
}).merge(circles).attr('cx', function (c) { | ||
}).merge(circleSelection).attr('r', function (c) { | ||
return c.r; | ||
}).attr('cx', function (c) { | ||
return c.x; | ||
@@ -264,9 +309,7 @@ }).attr('cy', function (c) { | ||
key: 'onSimulationEnd', | ||
value: function onSimulationEnd(circleData) { | ||
value: function onSimulationEnd(textSelection) { | ||
var _this2 = this; | ||
var svg = (0, _d3Selection.select)(this.node); | ||
var circles = svg.selectAll('circle'); | ||
circles.attr('class', this.getClasses('__circle')).attr('cx', function (c) { | ||
var circleSelection = (0, _d3Selection.select)(this.node).selectAll('circle'); | ||
circleSelection.attr('class', this.getClasses('__circle')).attr('cx', function (c) { | ||
if (c.center === 0) { | ||
@@ -301,14 +344,12 @@ return c.x; | ||
}).attr('fill-opacity', 0.8); | ||
var circleText = svg.selectAll('.circleText').data(circleData); | ||
circleText.exit().remove(); | ||
circleText.enter().append('text').attr('class', this.getClasses('__label')).attr('dx', function (d) { | ||
textSelection.enter().append('text').attr('class', this.getClasses('__label')).attr('text-anchor', 'middle').style('pointer-events', 'none').merge(textSelection).text(function (d, i) { | ||
var textKey = _this2.props.textKey || 'item_name'; | ||
var label = _this2.props.setCircleLabel ? _this2.props.setCircleLabel(d, i) : d[textKey]; | ||
return label; | ||
}).attr('dx', function (d) { | ||
return d.x; | ||
}).attr('dy', function (d) { | ||
return d.y + TEXT_LABEL_Y_OFFSET; | ||
}).attr('text-anchor', 'middle').style('pointer-events', 'none').text(function (d, i) { | ||
var textKey = _this2.props.textKey || 'item_name'; | ||
var label = _this2.props.setCircleLabel ? _this2.props.setCircleLabel(d, i) : d[textKey]; | ||
return label; | ||
}).merge(circleText).each(function setTextVisibility(d) { | ||
}).each(function setTextVisibility(d) { | ||
var diameter = 2 * d.r; | ||
@@ -320,3 +361,3 @@ var txt = (0, _d3Selection.select)(this); | ||
this.attachToolTip(); | ||
this.setState({ renderComplete: true }); | ||
this.setState({ renderComplete: true, dataChanged: false }); | ||
} | ||
@@ -354,2 +395,10 @@ }, { | ||
}, { | ||
key: 'setupCenters', | ||
value: function setupCenters(maxRadius) { | ||
var middle = this.props.areaWidth / 2; | ||
var right = Math.min(this.props.areaWidth * 2 / 3 + maxRadius, middle + maxRadius * 3); | ||
var left = Math.max(this.props.areaWidth / 6, middle - maxRadius * 3); | ||
return [middle, right, left]; | ||
} | ||
}, { | ||
key: 'render', | ||
@@ -417,11 +466,8 @@ value: function render() { | ||
key: 'prepareCircleData', | ||
value: function prepareCircleData(sizeScale) { | ||
var data = this.data.sort(function (a, b) { | ||
return b.r - a.r; | ||
value: function prepareCircleData(sizeScale, sortedData) { | ||
var valueKey = this.getValueKey(); | ||
var dataTotal = (0, _d3Array.sum)(sortedData, function (d) { | ||
return d[valueKey]; | ||
}); | ||
var valueKey = this.getValueKey(); | ||
var dataTotal = data.reduce(function (agg, item) { | ||
return agg + item[valueKey]; | ||
}, 0); | ||
return data.map(function (item, i) { | ||
return sortedData.map(function (item, i) { | ||
item.r = sizeScale(item[valueKey]); | ||
@@ -431,3 +477,3 @@ item.percentTotal = Math.round(item[valueKey] / dataTotal * 100); | ||
item.center = 0; | ||
} else if (i <= data.length / 2) { | ||
} else if (i <= sortedData.length / 2) { | ||
item.center = 1; | ||
@@ -445,24 +491,35 @@ } else { | ||
if (!this.scaleForSizeCache) { | ||
var valueKey = this.getValueKey(); | ||
var domainTop = data[0][valueKey]; | ||
var domainBottom = data[data.length - 1][valueKey]; | ||
if (domainBottom === domainTop) { | ||
domainBottom = 0; | ||
} | ||
var min = this.props.areaHeight / data.length / 4; | ||
var maxMultipler = 0.7; | ||
if (data.length > 2 && data[0][valueKey] - data[1][valueKey] < 3) { | ||
maxMultipler = 0.5; | ||
} | ||
var max = this.props.areaHeight * maxMultipler / 2; | ||
var scaleForSize = (0, _d3Scale.scaleSqrt)().domain([domainBottom, domainTop]).range([min, max]).clamp(true); | ||
var valueKey = this.getValueKey(); | ||
var domainTop = data[0][valueKey]; | ||
var height = this.props.areaHeight - (this.props.legendHeight || LEGEND_HEIGHT); | ||
var size = Math.min(height, this.props.areaWidth); | ||
var domainBottom = data[data.length - 1][valueKey]; | ||
if (domainBottom === domainTop) { | ||
domainBottom = 0; | ||
} | ||
var min = size / data.length / 4; | ||
var maxMultiplier = this.calcMaxMultiplier(data, this.props.areaWidth / height); | ||
var max = size * maxMultiplier / 2; | ||
var scaleForSize = (0, _d3Scale.scaleSqrt)().domain([domainBottom, domainTop]).range([min, max]).clamp(true); | ||
return scaleForSize; | ||
} | ||
}, { | ||
key: 'calcMaxMultiplier', | ||
value: function calcMaxMultiplier(data, aspectRatio) { | ||
var valueKey = this.getValueKey(); | ||
var maxMultiplier = 0.7; | ||
var minMultiplier = 0.5; | ||
if (aspectRatio < ASPECT_THRESHOLD) { | ||
maxMultiplier = 0.5; | ||
minMultiplier = 0.3; | ||
} | ||
if (!this.scaleForSizeCache && data.length) { | ||
this.scaleForSizeCache = scaleForSize; | ||
} | ||
return scaleForSize; | ||
var mean = (0, _d3Array.mean)(data, function (d) { | ||
return d[valueKey]; | ||
}); | ||
var range = data[0][valueKey] - data[data.length - 1][valueKey]; | ||
if (range && range <= mean) { | ||
return minMultiplier; | ||
} | ||
return this.scaleForSizeCache; | ||
return maxMultiplier; | ||
} | ||
@@ -480,2 +537,9 @@ }, { | ||
} | ||
}, { | ||
key: 'dataChanged', | ||
value: function dataChanged(props) { | ||
var lengthChanged = props.originalData.length !== this.data.length; | ||
var dataChanged = !(0, _lodash.isEqual)(props.originalData || [], this.data); | ||
return lengthChanged || dataChanged; | ||
} | ||
}]); | ||
@@ -499,3 +563,4 @@ return CircleDistribution; | ||
setLegendLabel: _propTypes2.default.func, | ||
onElementClick: _propTypes2.default.func | ||
onElementClick: _propTypes2.default.func, | ||
circleHighlightWidth: _propTypes2.default.number | ||
}; | ||
@@ -515,2 +580,6 @@ var _default = CircleDistribution; | ||
__REACT_HOT_LOADER__.register(ASPECT_THRESHOLD, 'ASPECT_THRESHOLD', 'src/charts/CircleDistribution.js'); | ||
__REACT_HOT_LOADER__.register(sizingProps, 'sizingProps', 'src/charts/CircleDistribution.js'); | ||
__REACT_HOT_LOADER__.register(CircleDistribution, 'CircleDistribution', 'src/charts/CircleDistribution.js'); | ||
@@ -517,0 +586,0 @@ |
{ | ||
"name": "nud3", | ||
"version": "0.5.19", | ||
"version": "0.5.20", | ||
"description": "New D3 composable charts for React", | ||
@@ -5,0 +5,0 @@ "repository": "https://github.com/nuvi/nud3", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
336010
8260
0