Comparing version 0.7.6 to 0.7.7
@@ -0,1 +1,12 @@ | ||
## v0.7.7 (July 15, 2016) | ||
### New Features | ||
* [#50]: Add `block.barOverlay` option to display bar charts proportional to block value | ||
* [#52]: Add `chart.totalCount` option to override total counts used in ratio calculations | ||
### Other | ||
* Simplify and clean up examples | ||
## v0.7.6 (July 12, 2016) | ||
@@ -2,0 +13,0 @@ |
@@ -102,2 +102,4 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -203,2 +205,3 @@ | ||
this.isCurved = settings.chart.curve.enabled; | ||
this.addValueOverlay = settings.block.barOverlay; | ||
this.curveHeight = settings.chart.curve.height; | ||
@@ -211,2 +214,3 @@ this.fillType = settings.block.fill.type; | ||
this.animation = settings.chart.animate; | ||
this.totalCount = settings.chart.totalCount; | ||
@@ -304,2 +308,4 @@ // Support for events | ||
* | ||
* @param {Array} data | ||
* | ||
* @return {Number} | ||
@@ -313,2 +319,6 @@ */ | ||
if (this.totalCount !== null) { | ||
return this.totalCount || 0; | ||
} | ||
var total = 0; | ||
@@ -341,3 +351,3 @@ | ||
var count = _this2._getRawBlockCount(block); | ||
var ratio = count / totalCount; | ||
var ratio = count / totalCount || 0; | ||
var label = block[0]; | ||
@@ -418,3 +428,5 @@ | ||
this.blockPaths = this._makePaths(); | ||
var newPaths = this._makePaths(); | ||
this.blockPaths = newPaths[0]; | ||
this.overlayPaths = newPaths[1]; | ||
@@ -439,3 +451,3 @@ // Define color gradients | ||
* | ||
* @return {Array} | ||
* @return {Array, Array} | ||
*/ | ||
@@ -449,2 +461,3 @@ | ||
var paths = []; | ||
var overlayPaths = []; | ||
@@ -471,3 +484,3 @@ // Initialize velocity | ||
var middle = this.width / 2; | ||
var centerX = this.width / 2; | ||
@@ -566,3 +579,3 @@ // Move down if there is an initial curve | ||
var widthPercent = 1 - nextBlockValue / block.value; | ||
dx = widthPercent * (middle - prevLeftX); | ||
dx = widthPercent * (centerX - prevLeftX); | ||
} | ||
@@ -604,27 +617,27 @@ | ||
// Plot curved lines | ||
var dimensions = { | ||
centerX: centerX, | ||
prevLeftX: prevLeftX, | ||
prevRightX: prevRightX, | ||
prevHeight: prevHeight, | ||
nextLeftX: nextLeftX, | ||
nextRightX: nextRightX, | ||
nextHeight: nextHeight, | ||
curveHeight: _this3.curveHeight, | ||
ratio: block.ratio | ||
}; | ||
if (_this3.isCurved) { | ||
paths.push([ | ||
// Top Bezier curve | ||
[prevLeftX, prevHeight, 'M'], [middle, prevHeight + (_this3.curveHeight - 10), 'Q'], [prevRightX, prevHeight, ''], | ||
// Right line | ||
[nextRightX, nextHeight, 'L'], | ||
// Bottom Bezier curve | ||
[nextRightX, nextHeight, 'M'], [middle, nextHeight + _this3.curveHeight, 'Q'], [nextLeftX, nextHeight, ''], | ||
// Left line | ||
[prevLeftX, prevHeight, 'L']]); | ||
// Plot straight lines | ||
paths = [].concat(_toConsumableArray(paths), [_this3.navigator.makeCurvedPaths(dimensions)]); | ||
if (_this3.addValueOverlay) { | ||
overlayPaths = [].concat(_toConsumableArray(overlayPaths), [_this3.navigator.makeCurvedPaths(dimensions, true)]); | ||
} | ||
} else { | ||
paths.push([ | ||
// Start position | ||
[prevLeftX, prevHeight, 'M'], | ||
// Move to right | ||
[prevRightX, prevHeight, 'L'], | ||
// Move down | ||
[nextRightX, nextHeight, 'L'], | ||
// Move to left | ||
[nextLeftX, nextHeight, 'L'], | ||
// Wrap back to top | ||
[prevLeftX, prevHeight, 'L']]); | ||
paths = [].concat(_toConsumableArray(paths), [_this3.navigator.makeStraightPaths(dimensions)]); | ||
if (_this3.addValueOverlay) { | ||
overlayPaths = [].concat(_toConsumableArray(overlayPaths), [_this3.navigator.makeStraightPaths(dimensions, true)]); | ||
} | ||
} | ||
@@ -637,3 +650,3 @@ // Set the next block's previous position | ||
return paths; | ||
return [paths, overlayPaths]; | ||
} | ||
@@ -736,15 +749,47 @@ | ||
var overlayPath = null; | ||
var pathColor = this.blocks[index].fill.actual; | ||
if (this.addValueOverlay) { | ||
overlayPath = this._getOverlayPath(group, index); | ||
this._attachData(overlayPath, this.blocks[index]); | ||
// Add data attribute to distinguish between paths | ||
path.node().setAttribute('pathType', 'background'); | ||
overlayPath.node().setAttribute('pathType', 'foreground'); | ||
// Default path becomes background of lighter shade | ||
pathColor = this.colorizer.shade(this.blocks[index].fill.raw, 0.3); | ||
} | ||
// Add animation components | ||
if (this.animation !== 0) { | ||
path.transition().duration(this.animation).ease('linear').attr('fill', this.blocks[index].fill.actual).attr('d', this._getPathDefinition(index)).each('end', function () { | ||
path.transition().duration(this.animation).ease('linear').attr('fill', pathColor).attr('d', this._getPathDefinition(index)).each('end', function () { | ||
_this5._drawBlock(index + 1); | ||
}); | ||
} else { | ||
path.attr('fill', this.blocks[index].fill.actual).attr('d', this._getPathDefinition(index)); | ||
path.attr('fill', pathColor).attr('d', this._getPathDefinition(index)); | ||
this._drawBlock(index + 1); | ||
} | ||
// Add path overlay | ||
if (this.addValueOverlay) { | ||
path.attr('stroke', this.blocks[index].fill.raw); | ||
if (this.animation !== 0) { | ||
overlayPath.transition().duration(this.animation).ease('linear').attr('fill', this.blocks[index].fill.actual).attr('d', this._getOverlayPathDefinition(index)); | ||
} else { | ||
overlayPath.attr('fill', this.blocks[index].fill.actual).attr('d', this._getOverlayPathDefinition(index)); | ||
} | ||
} | ||
// Add the hover events | ||
if (this.hoverEffects) { | ||
path.on('mouseover', this._onMouseOver.bind(this)).on('mouseout', this._onMouseOut.bind(this)); | ||
[path, overlayPath].forEach(function (target) { | ||
if (!target) { | ||
return; | ||
} | ||
target.on('mouseover', _this5._onMouseOver.bind(_this5)).on('mouseout', _this5._onMouseOut.bind(_this5)); | ||
}); | ||
} | ||
@@ -754,3 +799,9 @@ | ||
if (this.onBlockClick !== null) { | ||
path.on('click', this.onBlockClick); | ||
[path, overlayPath].forEach(function (target) { | ||
if (!target) { | ||
return; | ||
} | ||
target.on('click', _this5.onBlockClick); | ||
}); | ||
} | ||
@@ -781,6 +832,26 @@ | ||
/** | ||
* @param {Object} group | ||
* @param {int} index | ||
* | ||
* @return {Object} | ||
*/ | ||
}, { | ||
key: '_getOverlayPath', | ||
value: function _getOverlayPath(group, index) { | ||
var path = group.append('path'); | ||
if (this.animation !== 0) { | ||
this._addBeforeTransition(path, index, true); | ||
} | ||
return path; | ||
} | ||
/** | ||
* Set the attributes of a path element before its animation. | ||
* | ||
* @param {Object} path | ||
* @param {int} index | ||
* @param {Object} path | ||
* @param {int} index | ||
* @param {boolean} isOverlay | ||
* | ||
@@ -792,4 +863,4 @@ * @return {void} | ||
key: '_addBeforeTransition', | ||
value: function _addBeforeTransition(path, index) { | ||
var paths = this.blockPaths[index]; | ||
value: function _addBeforeTransition(path, index, isOverlay) { | ||
var paths = isOverlay ? this.overlayPaths[index] : this.blockPaths[index]; | ||
@@ -857,2 +928,20 @@ var beforePath = ''; | ||
/** | ||
* @param {int} index | ||
* | ||
* @return {string} | ||
*/ | ||
}, { | ||
key: '_getOverlayPathDefinition', | ||
value: function _getOverlayPathDefinition(index) { | ||
var commands = []; | ||
this.overlayPaths[index].forEach(function (command) { | ||
commands.push([command[2], command[0], command[1]]); | ||
}); | ||
return this.navigator.plot(commands); | ||
} | ||
/** | ||
* @param {Object} data | ||
@@ -866,3 +955,18 @@ * | ||
value: function _onMouseOver(data) { | ||
_d2.default.select(_d2.default.event.target).attr('fill', this.colorizer.shade(data.fill.raw, -0.2)); | ||
var children = _d2.default.event.target.parentElement.childNodes; | ||
for (var i = 0; i < children.length; i++) { | ||
// Highlight all paths within one block | ||
var node = children[i]; | ||
if (node.nodeName.toLowerCase() === 'path') { | ||
var type = node.getAttribute('pathType') || ''; | ||
if (type === 'foreground') { | ||
_d2.default.select(node).attr('fill', this.colorizer.shade(data.fill.raw, -0.5)); | ||
} else { | ||
_d2.default.select(node).attr('fill', this.colorizer.shade(data.fill.raw, -0.2)); | ||
} | ||
} | ||
} | ||
} | ||
@@ -879,3 +983,19 @@ | ||
value: function _onMouseOut(data) { | ||
_d2.default.select(_d2.default.event.target).attr('fill', data.fill.actual); | ||
var children = _d2.default.event.target.parentElement.childNodes; | ||
for (var i = 0; i < children.length; i++) { | ||
// Restore original color for all paths of a block | ||
var node = children[i]; | ||
if (node.nodeName.toLowerCase() === 'path') { | ||
var type = node.getAttribute('pathType') || ''; | ||
if (type === 'background') { | ||
var backgroundColor = this.colorizer.shade(data.fill.raw, 0.3); | ||
_d2.default.select(node).attr('fill', backgroundColor); | ||
} else { | ||
_d2.default.select(node).attr('fill', data.fill.actual); | ||
} | ||
} | ||
} | ||
} | ||
@@ -886,2 +1006,3 @@ | ||
* @param {int} index | ||
* | ||
* @return {void} | ||
@@ -950,3 +1071,4 @@ */ | ||
height: 20 | ||
} | ||
}, | ||
totalCount: null | ||
}, | ||
@@ -956,2 +1078,3 @@ block: { | ||
dynamicSlope: false, | ||
barOverlay: false, | ||
fill: { | ||
@@ -1327,2 +1450,245 @@ scale: _d2.default.scale.category10().domain(_d2.default.range(0, 10)), | ||
} | ||
/** | ||
* @param {Object} dimensions | ||
* @param {boolean} isValueOverlay | ||
* | ||
* @return {Array} | ||
*/ | ||
}, { | ||
key: 'makeCurvedPaths', | ||
value: function makeCurvedPaths(dimensions) { | ||
var isValueOverlay = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; | ||
var points = this.makeBezierPoints(dimensions); | ||
if (isValueOverlay) { | ||
return this.makeBezierPath(points, dimensions.ratio); | ||
} | ||
return this.makeBezierPath(points); | ||
} | ||
/** | ||
* @param {Number} centerX | ||
* @param {Number} prevLeftX | ||
* @param {Number} prevRightX | ||
* @param {Number} prevHeight | ||
* @param {Number} nextLeftX | ||
* @param {Number} nextRightX | ||
* @param {Number} nextHeight | ||
* @param {Number} curveHeight | ||
* | ||
* @returns {Object} | ||
*/ | ||
}, { | ||
key: 'makeBezierPoints', | ||
value: function makeBezierPoints(_ref) { | ||
var centerX = _ref.centerX; | ||
var prevLeftX = _ref.prevLeftX; | ||
var prevRightX = _ref.prevRightX; | ||
var prevHeight = _ref.prevHeight; | ||
var nextLeftX = _ref.nextLeftX; | ||
var nextRightX = _ref.nextRightX; | ||
var nextHeight = _ref.nextHeight; | ||
var curveHeight = _ref.curveHeight; | ||
return { | ||
p00: { | ||
x: prevLeftX, | ||
y: prevHeight | ||
}, | ||
p01: { | ||
x: centerX, | ||
y: prevHeight + curveHeight - 10 | ||
}, | ||
p02: { | ||
x: prevRightX, | ||
y: prevHeight | ||
}, | ||
p10: { | ||
x: nextLeftX, | ||
y: nextHeight | ||
}, | ||
p11: { | ||
x: centerX, | ||
y: nextHeight + curveHeight | ||
}, | ||
p12: { | ||
x: nextRightX, | ||
y: nextHeight | ||
} | ||
}; | ||
} | ||
/** | ||
* @param {Object} p00 | ||
* @param {Object} p01 | ||
* @param {Object} p02 | ||
* @param {Object} p10 | ||
* @param {Object} p11 | ||
* @param {Object} p12 | ||
* @param {Number} ratio | ||
* | ||
* @returns {Array} | ||
*/ | ||
}, { | ||
key: 'makeBezierPath', | ||
value: function makeBezierPath(_ref2) { | ||
var p00 = _ref2.p00; | ||
var p01 = _ref2.p01; | ||
var p02 = _ref2.p02; | ||
var p10 = _ref2.p10; | ||
var p11 = _ref2.p11; | ||
var p12 = _ref2.p12; | ||
var ratio = arguments.length <= 1 || arguments[1] === undefined ? 1 : arguments[1]; | ||
var curve0 = this.getQuadraticBezierCurve(p00, p01, p02, ratio); | ||
var curve1 = this.getQuadraticBezierCurve(p10, p11, p12, ratio); | ||
return [ | ||
// Top Bezier curve | ||
[curve0.p0.x, curve0.p0.y, 'M'], [curve0.p1.x, curve0.p1.y, 'Q'], [curve0.p2.x, curve0.p2.y, ''], | ||
// Right line | ||
[curve1.p2.x, curve1.p2.y, 'L'], | ||
// Bottom Bezier curve | ||
[curve1.p2.x, curve1.p2.y, 'M'], [curve1.p1.x, curve1.p1.y, 'Q'], [curve1.p0.x, curve1.p0.y, ''], | ||
// Left line | ||
[curve0.p0.x, curve0.p0.y, 'L']]; | ||
} | ||
/** | ||
* @param {Object} p0 | ||
* @param {Object} p1 | ||
* @param {Object} p2 | ||
* @param {Number} t | ||
* | ||
* @return {Object} | ||
*/ | ||
}, { | ||
key: 'getQuadraticBezierCurve', | ||
value: function getQuadraticBezierCurve(p0, p1, p2) { | ||
var t = arguments.length <= 3 || arguments[3] === undefined ? 1 : arguments[3]; | ||
// Quadratic Bezier curve syntax: M(P0) Q(P1) P2 | ||
// Where P0, P2 are the curve endpoints and P1 is the control point | ||
// More generally, at 0 <= t <= 1, we have the following: | ||
// Q0(t), which varies linearly from P0 to P1 | ||
// Q1(t), which varies linearly from P1 to P2 | ||
// B(t), which is interpolated linearly between Q0(t) and Q1(t) | ||
// For an intermediate curve at 0 <= t <= 1: | ||
// P1(t) = Q0(t) | ||
// P2(t) = B(t) | ||
return { | ||
p0: p0, | ||
p1: { | ||
x: this.getLinearInterpolation(p0, p1, t, 'x'), | ||
y: this.getLinearInterpolation(p0, p1, t, 'y') | ||
}, | ||
p2: { | ||
x: this.getQuadraticInterpolation(p0, p1, p2, t, 'x'), | ||
y: this.getQuadraticInterpolation(p0, p1, p2, t, 'y') | ||
} | ||
}; | ||
} | ||
/** | ||
* @param {Object} p0 | ||
* @param {Object} p1 | ||
* @param {Number} t | ||
* @param {string} axis | ||
* | ||
* @return {Number} | ||
*/ | ||
}, { | ||
key: 'getLinearInterpolation', | ||
value: function getLinearInterpolation(p0, p1, t, axis) { | ||
return p0[axis] + t * (p1[axis] - p0[axis]); | ||
} | ||
/** | ||
* @param {Object} p0 | ||
* @param {Object} p1 | ||
* @param {Object} p2 | ||
* @param {Number} t | ||
* @param {string} axis | ||
* | ||
* @return {Number} | ||
*/ | ||
}, { | ||
key: 'getQuadraticInterpolation', | ||
value: function getQuadraticInterpolation(p0, p1, p2, t, axis) { | ||
return Math.pow(1 - t, 2) * p0[axis] + 2 * (1 - t) * t * p1[axis] + Math.pow(t, 2) * p2[axis]; | ||
} | ||
/** | ||
* @param {Number} prevLeftX | ||
* @param {Number} prevRightX | ||
* @param {Number} prevHeight | ||
* @param {Number} nextLeftX | ||
* @param {Number} nextRightX | ||
* @param {Number} nextHeight | ||
* @param {Number} ratio | ||
* @param {boolean} isValueOverlay | ||
* | ||
* @return {Object} | ||
*/ | ||
}, { | ||
key: 'makeStraightPaths', | ||
value: function makeStraightPaths(_ref3) { | ||
var prevLeftX = _ref3.prevLeftX; | ||
var prevRightX = _ref3.prevRightX; | ||
var prevHeight = _ref3.prevHeight; | ||
var nextLeftX = _ref3.nextLeftX; | ||
var nextRightX = _ref3.nextRightX; | ||
var nextHeight = _ref3.nextHeight; | ||
var ratio = _ref3.ratio; | ||
var isValueOverlay = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; | ||
if (isValueOverlay) { | ||
var lengthTop = prevRightX - prevLeftX; | ||
var lengthBtm = nextRightX - nextLeftX; | ||
var rightSideTop = lengthTop * (ratio || 0) + prevLeftX; | ||
var rightSideBtm = lengthBtm * (ratio || 0) + nextLeftX; | ||
// Overlay should not be longer than the max length of the path | ||
rightSideTop = Math.min(rightSideTop, lengthTop); | ||
rightSideBtm = Math.min(rightSideBtm, lengthBtm); | ||
return [ | ||
// Start position | ||
[prevLeftX, prevHeight, 'M'], | ||
// Move to right | ||
[rightSideTop, prevHeight, 'L'], | ||
// Move down | ||
[rightSideBtm, nextHeight, 'L'], | ||
// Move to left | ||
[nextLeftX, nextHeight, 'L'], | ||
// Wrap back to top | ||
[prevLeftX, prevHeight, 'L']]; | ||
} | ||
return [ | ||
// Start position | ||
[prevLeftX, prevHeight, 'M'], | ||
// Move to right | ||
[prevRightX, prevHeight, 'L'], | ||
// Move down | ||
[nextRightX, nextHeight, 'L'], | ||
// Move to left | ||
[nextLeftX, nextHeight, 'L'], | ||
// Wrap back to top | ||
[prevLeftX, prevHeight, 'L']]; | ||
} | ||
}]); | ||
@@ -1329,0 +1695,0 @@ |
@@ -1,2 +0,2 @@ | ||
/*! d3-funnel - v0.7.6 | 2016 */ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("d3")):"function"==typeof define&&define.amd?define(["d3"],e):"object"==typeof exports?exports.D3Funnel=e(require("d3")):t.D3Funnel=e(t.d3)}(this,function(t){return function(t){function e(n){if(i[n])return i[n].exports;var a=i[n]={exports:{},id:n,loaded:!1};return t[n].call(a.exports,a,a.exports,e),a.loaded=!0,a.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){"use strict";t.exports=i(1)["default"]},function(t,e,i){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function a(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var n in i)Object.prototype.hasOwnProperty.call(i,n)&&(t[n]=i[n])}return t},o=function(){function t(t,e){for(var i=0;i<e.length;i++){var n=e[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,i,n){return i&&t(e.prototype,i),n&&t(e,n),e}}(),l=i(2),s=n(l),h=i(3),c=n(h),u=i(4),f=n(u),d=i(5),v=n(d),b=i(6),y=n(b),g=function(){function t(e){a(this,t),this.selector=e,this.colorizer=new c["default"],this.labelFormatter=new f["default"],this.navigator=new v["default"]}return o(t,[{key:"destroy",value:function(){var t=s["default"].select(this.selector);t.selectAll("svg").remove(),t.selectAll("*").remove(),t.text("")}},{key:"draw",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];this.destroy(),this._initialize(t,e),this._draw()}},{key:"_initialize",value:function(t,e){this._validateData(t);var i=this._getSettings(e);this.label=i.label,this.labelFormatter.setFormat(this.label.format),this.colorizer.setLabelFill(i.label.fill),this.colorizer.setScale(i.block.fill.scale),this.width=i.chart.width,this.height=i.chart.height,this.bottomWidth=i.chart.width*i.chart.bottomWidth,this.bottomPinch=i.chart.bottomPinch,this.isInverted=i.chart.inverted,this.isCurved=i.chart.curve.enabled,this.curveHeight=i.chart.curve.height,this.fillType=i.block.fill.type,this.hoverEffects=i.block.highlight,this.dynamicHeight=i.block.dynamicHeight,this.dynamicSlope=i.block.dynamicSlope,this.minHeight=i.block.minHeight,this.animation=i.chart.animate,this.onBlockClick=i.events.click.block,this._setBlocks(t),this.bottomLeftX=(this.width-this.bottomWidth)/2,this.dx=this._getDx(),this.dy=this._getDy()}},{key:"_validateData",value:function(t){if(Array.isArray(t)===!1)throw new Error("Data must be an array.");if(0===t.length)throw new Error("Data array must contain at least one element.");if(Array.isArray(t[0])===!1)throw new Error("Data array elements must be arrays.");if(t[0].length<2)throw new Error("Data array elements must contain a label and value.")}},{key:"_getSettings",value:function(e){var i=y["default"].extend({},t.defaults);return i.chart.width=parseInt(s["default"].select(this.selector).style("width"),10),i.chart.height=parseInt(s["default"].select(this.selector).style("height"),10),i=y["default"].extend(i,e),i.chart.width<=0&&(i.chart.width=t.defaults.chart.width),i.chart.height<=0&&(i.chart.height=t.defaults.chart.height),i}},{key:"_setBlocks",value:function(t){var e=this._getTotalCount(t);this.blocks=this._standardizeData(t,e)}},{key:"_getTotalCount",value:function(t){var e=this,i=0;return t.forEach(function(t){i+=e._getRawBlockCount(t)}),i}},{key:"_standardizeData",value:function(t,e){var i=this,n=[];return t.forEach(function(t,a){var r=i._getRawBlockCount(t),o=r/e,l=t[0];n.push({index:a,ratio:o,value:r,height:i.height*o,fill:i.colorizer.getBlockFill(t,a,i.fillType),label:{raw:l,formatted:i.labelFormatter.format(l,t[1]),color:i.colorizer.getLabelFill(t)}})}),n}},{key:"_getRawBlockCount",value:function(t){return Array.isArray(t[1])?t[1][0]:t[1]}},{key:"_getDx",value:function(){return this.bottomPinch>0?this.bottomLeftX/(this.blocks.length-this.bottomPinch):this.bottomLeftX/this.blocks.length}},{key:"_getDy",value:function(){return this.isCurved?(this.height-this.curveHeight)/this.blocks.length:this.height/this.blocks.length}},{key:"_draw",value:function(){this.svg=s["default"].select(this.selector).append("svg").attr("width",this.width).attr("height",this.height),this.blockPaths=this._makePaths(),"gradient"===this.fillType&&this._defineColorGradients(this.svg),this.isCurved&&this._drawTopOval(this.svg,this.blockPaths),this._drawBlock(0)}},{key:"_makePaths",value:function(){var t=this,e=[],i=this.dx,n=this.dy,a=0,r=this.width,o=0;this.isInverted&&(a=this.bottomLeftX,r=this.width-this.bottomLeftX);var l=0,s=0,h=0,c=this.width/2;this.isCurved&&(o=10);var u=this.height;0!==this.minHeight&&(u=this.height-this.minHeight*this.blocks.length);var f=this.height;this.blocks.forEach(function(e,i){t.bottomPinch>0&&(t.isInverted?i<t.bottomPinch&&(f-=e.height):i>=t.blocks.length-t.bottomPinch&&(f-=e.height))});var d=2*f/(this.width-this.bottomWidth);return this.blocks.forEach(function(f,v){if(t.dynamicHeight&&(n=u*f.ratio,0!==t.minHeight&&(n+=t.minHeight),t.isCurved&&(n-=t.curveHeight/t.blocks.length),l=(o+n)/d,t.isInverted&&(l=(o+n-t.height)/(-1*d)),0===t.bottomWidth&&v===t.blocks.length-1&&(l=t.width/2,t.isInverted&&(l=0)),t.bottomWidth===t.width&&(l=a),i=l-a,t.isInverted&&(i=a-l)),t.dynamicSlope){var b=t.blocks[v+1]?t.blocks[v+1].value:f.value,y=1-b/f.value;i=y*(c-a)}t.bottomPinch>0&&(t.isInverted?(t.dynamicHeight||(i=t.dx),i=v<t.bottomPinch?0:i):v>=t.blocks.length-t.bottomPinch&&(i=0)),l=a+i,s=r-i,h=o+n,t.isInverted&&(l=a-i,s=r+i),t.isCurved?e.push([[a,o,"M"],[c,o+(t.curveHeight-10),"Q"],[r,o,""],[s,h,"L"],[s,h,"M"],[c,h+t.curveHeight,"Q"],[l,h,""],[a,o,"L"]]):e.push([[a,o,"M"],[r,o,"L"],[s,h,"L"],[l,h,"L"],[a,o,"L"]]),a=l,r=s,o=h}),e}},{key:"_defineColorGradients",value:function(t){var e=this,i=t.append("defs");this.blocks.forEach(function(t,n){var a=t.fill.raw,r=e.colorizer.shade(a,-.2),o=i.append("linearGradient").attr({id:"gradient-"+n}),l=[[0,r],[40,a],[60,a],[100,r]];l.forEach(function(t){o.append("stop").attr({offset:t[0]+"%",style:"stop-color: "+t[1]})})})}},{key:"_drawTopOval",value:function(t,e){var i=0,n=this.width,a=this.width/2;this.isInverted&&(i=this.bottomLeftX,n=this.width-this.bottomLeftX);var r=e[0],o=r[1][1]+this.curveHeight-10,l=this.navigator.plot([["M",i,r[0][1]],["Q",a,o],[" ",n,r[2][1]],["M",n,10],["Q",a,0],[" ",i,10]]);t.append("path").attr("fill",this.colorizer.shade(this.blocks[0].fill.raw,-.4)).attr("d",l)}},{key:"_drawBlock",value:function(t){var e=this;if(t!==this.blocks.length){var i=this.svg.append("g"),n=this._getBlockPath(i,t);this._attachData(n,this.blocks[t]),0!==this.animation?n.transition().duration(this.animation).ease("linear").attr("fill",this.blocks[t].fill.actual).attr("d",this._getPathDefinition(t)).each("end",function(){e._drawBlock(t+1)}):(n.attr("fill",this.blocks[t].fill.actual).attr("d",this._getPathDefinition(t)),this._drawBlock(t+1)),this.hoverEffects&&n.on("mouseover",this._onMouseOver.bind(this)).on("mouseout",this._onMouseOut.bind(this)),null!==this.onBlockClick&&n.on("click",this.onBlockClick),this._addBlockLabel(i,t)}}},{key:"_getBlockPath",value:function(t,e){var i=t.append("path");return 0!==this.animation&&this._addBeforeTransition(i,e),i}},{key:"_addBeforeTransition",value:function(t,e){var i=this.blockPaths[e],n="",a="";n=this.isCurved?this.navigator.plot([["M",i[0][0],i[0][1]],["Q",i[1][0],i[1][1]],[" ",i[2][0],i[2][1]],["L",i[2][0],i[2][1]],["M",i[2][0],i[2][1]],["Q",i[1][0],i[1][1]],[" ",i[0][0],i[0][1]]]):this.navigator.plot([["M",i[0][0],i[0][1]],["L",i[1][0],i[1][1]],["L",i[1][0],i[1][1]],["L",i[0][0],i[0][1]]]),a="solid"===this.fillType&&e>0?this.blocks[e-1].fill.actual:this.blocks[e].fill.actual,t.attr("d",n).attr("fill",a)}},{key:"_attachData",value:function(t,e){var i=r({},e,{node:t.node()});t.data([i])}},{key:"_getPathDefinition",value:function(t){var e=[];return this.blockPaths[t].forEach(function(t){e.push([t[2],t[0],t[1]])}),this.navigator.plot(e)}},{key:"_onMouseOver",value:function(t){s["default"].select(s["default"].event.target).attr("fill",this.colorizer.shade(t.fill.raw,-.2))}},{key:"_onMouseOut",value:function(t){s["default"].select(s["default"].event.target).attr("fill",t.fill.actual)}},{key:"_addBlockLabel",value:function(t,e){var i=this.blockPaths[e],n=this.blocks[e].label.formatted,a=this.blocks[e].label.color,r=this.width/2,o=this._getTextY(i),l=t.append("text").text(n).attr({x:r,y:o,fill:a,"text-anchor":"middle","dominant-baseline":"middle","pointer-events":"none"}).attr("font-size",this.label.fontSize);null!==this.label.fontFamily&&l.attr("font-family",this.label.fontFamily)}},{key:"_getTextY",value:function(t){return this.isCurved?(t[2][1]+t[3][1])/2+this.curveHeight/this.blocks.length:(t[1][1]+t[2][1])/2}}]),t}();g.defaults={chart:{width:350,height:400,bottomWidth:1/3,bottomPinch:0,inverted:!1,horizontal:!1,animate:0,curve:{enabled:!1,height:20}},block:{dynamicHeight:!1,dynamicSlope:!1,fill:{scale:s["default"].scale.category10().domain(s["default"].range(0,10)),type:"solid"},minHeight:0,highlight:!1},label:{fontFamily:null,fontSize:"14px",fill:"#fff",format:"{l}: {f}"},events:{click:{block:null}}},e["default"]=g},function(e,i){e.exports=t},function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){for(var i=0;i<e.length;i++){var n=e[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,i,n){return i&&t(e.prototype,i),n&&t(e,n),e}}(),a=function(){function t(){i(this,t),this.hexExpression=/^#([0-9a-f]{3}|[0-9a-f]{6})$/i,this.labelFill=null,this.scale=null}return n(t,[{key:"setLabelFill",value:function(t){this.labelFill=t}},{key:"setScale",value:function(t){this.scale=t}},{key:"getBlockFill",value:function(t,e,i){var n=this.getBlockRawFill(t,e);return{raw:n,actual:this.getBlockActualFill(n,e,i)}}},{key:"getBlockRawFill",value:function(t,e){return t.length>2&&this.hexExpression.test(t[2])?t[2]:Array.isArray(this.scale)?this.scale[e]:this.scale(e)}},{key:"getBlockActualFill",value:function(t,e,i){return"solid"===i?t:"url(#gradient-"+e+")"}},{key:"getLabelFill",value:function(t){return t.length>3&&this.hexExpression.test(t[3])?t[3]:this.labelFill}},{key:"shade",value:function(t,e){var i=t.slice(1);3===i.length&&(i=this.expandHex(i));var n=parseInt(i,16),a=0>e?0:255,r=0>e?-1*e:e,o=n>>16,l=n>>8&255,s=255&n,h=16777216+65536*(Math.round((a-o)*r)+o)+256*(Math.round((a-l)*r)+l)+(Math.round((a-s)*r)+s);return"#"+h.toString(16).slice(1)}},{key:"expandHex",value:function(t){return t[0]+t[0]+t[1]+t[1]+t[2]+t[2]}}]),t}();e["default"]=a},function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){for(var i=0;i<e.length;i++){var n=e[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,i,n){return i&&t(e.prototype,i),n&&t(e,n),e}}(),a=function(){function t(){i(this,t),this.expression=null}return n(t,[{key:"setFormat",value:function(t){"function"==typeof t?this.formatter=t:(this.expression=t,this.formatter=this.stringFormatter)}},{key:"format",value:function(t,e){return Array.isArray(e)?this.formatter(t,e[0],e[1]):this.formatter(t,e,null)}},{key:"stringFormatter",value:function(t,e){var i=arguments.length<=2||void 0===arguments[2]?null:arguments[2],n=i;return null===i&&(n=this.getDefaultFormattedValue(e)),this.expression.split("{l}").join(t).split("{v}").join(e).split("{f}").join(n)}},{key:"getDefaultFormattedValue",value:function(t){return t.toLocaleString()}}]),t}();e["default"]=a},function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){for(var i=0;i<e.length;i++){var n=e[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,i,n){return i&&t(e.prototype,i),n&&t(e,n),e}}(),a=function(){function t(){i(this,t)}return n(t,[{key:"plot",value:function(t){var e="";return t.forEach(function(t){e+=""+t[0]+t[1]+","+t[2]+" "}),e.replace(/ +/g," ").trim()}}]),t}();e["default"]=a},function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol?"symbol":typeof t},a=function(){function t(t,e){for(var i=0;i<e.length;i++){var n=e[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,i,n){return i&&t(e.prototype,i),n&&t(e,n),e}}(),r=function(){function t(){i(this,t)}return a(t,null,[{key:"isExtendableObject",value:function(t){return"object"===("undefined"==typeof t?"undefined":n(t))&&null!==t&&!Array.isArray(t)}},{key:"extend",value:function(e,i){var n={};return Object.keys(e).length>0&&(n=t.extend({},e)),Object.keys(i).forEach(function(a){t.isExtendableObject(i[a])?t.isExtendableObject(e[a])?n[a]=t.extend(e[a],i[a]):n[a]=t.extend({},i[a]):n[a]=i[a]}),n}}]),t}();e["default"]=r}])}); | ||
/*! d3-funnel - v0.7.7 | 2016 */ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("d3")):"function"==typeof define&&define.amd?define(["d3"],e):"object"==typeof exports?exports.D3Funnel=e(require("d3")):t.D3Funnel=e(t.d3)}(this,function(t){return function(t){function e(a){if(i[a])return i[a].exports;var n=i[a]={exports:{},id:a,loaded:!1};return t[a].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){"use strict";t.exports=i(1)["default"]},function(t,e,i){"use strict";function a(t){return t&&t.__esModule?t:{"default":t}}function n(t){if(Array.isArray(t)){for(var e=0,i=Array(t.length);e<t.length;e++)i[e]=t[e];return i}return Array.from(t)}function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var a in i)Object.prototype.hasOwnProperty.call(i,a)&&(t[a]=i[a])}return t},l=function(){function t(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}return function(e,i,a){return i&&t(e.prototype,i),a&&t(e,a),e}}(),h=i(2),s=a(h),u=i(3),c=a(u),f=i(4),d=a(f),v=i(5),y=a(v),g=i(6),p=a(g),b=function(){function t(e){r(this,t),this.selector=e,this.colorizer=new c["default"],this.labelFormatter=new d["default"],this.navigator=new y["default"]}return l(t,[{key:"destroy",value:function(){var t=s["default"].select(this.selector);t.selectAll("svg").remove(),t.selectAll("*").remove(),t.text("")}},{key:"draw",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];this.destroy(),this._initialize(t,e),this._draw()}},{key:"_initialize",value:function(t,e){this._validateData(t);var i=this._getSettings(e);this.label=i.label,this.labelFormatter.setFormat(this.label.format),this.colorizer.setLabelFill(i.label.fill),this.colorizer.setScale(i.block.fill.scale),this.width=i.chart.width,this.height=i.chart.height,this.bottomWidth=i.chart.width*i.chart.bottomWidth,this.bottomPinch=i.chart.bottomPinch,this.isInverted=i.chart.inverted,this.isCurved=i.chart.curve.enabled,this.addValueOverlay=i.block.barOverlay,this.curveHeight=i.chart.curve.height,this.fillType=i.block.fill.type,this.hoverEffects=i.block.highlight,this.dynamicHeight=i.block.dynamicHeight,this.dynamicSlope=i.block.dynamicSlope,this.minHeight=i.block.minHeight,this.animation=i.chart.animate,this.totalCount=i.chart.totalCount,this.onBlockClick=i.events.click.block,this._setBlocks(t),this.bottomLeftX=(this.width-this.bottomWidth)/2,this.dx=this._getDx(),this.dy=this._getDy()}},{key:"_validateData",value:function(t){if(Array.isArray(t)===!1)throw new Error("Data must be an array.");if(0===t.length)throw new Error("Data array must contain at least one element.");if(Array.isArray(t[0])===!1)throw new Error("Data array elements must be arrays.");if(t[0].length<2)throw new Error("Data array elements must contain a label and value.")}},{key:"_getSettings",value:function(e){var i=p["default"].extend({},t.defaults);return i.chart.width=parseInt(s["default"].select(this.selector).style("width"),10),i.chart.height=parseInt(s["default"].select(this.selector).style("height"),10),i=p["default"].extend(i,e),i.chart.width<=0&&(i.chart.width=t.defaults.chart.width),i.chart.height<=0&&(i.chart.height=t.defaults.chart.height),i}},{key:"_setBlocks",value:function(t){var e=this._getTotalCount(t);this.blocks=this._standardizeData(t,e)}},{key:"_getTotalCount",value:function(t){var e=this;if(null!==this.totalCount)return this.totalCount||0;var i=0;return t.forEach(function(t){i+=e._getRawBlockCount(t)}),i}},{key:"_standardizeData",value:function(t,e){var i=this,a=[];return t.forEach(function(t,n){var r=i._getRawBlockCount(t),o=r/e||0,l=t[0];a.push({index:n,ratio:o,value:r,height:i.height*o,fill:i.colorizer.getBlockFill(t,n,i.fillType),label:{raw:l,formatted:i.labelFormatter.format(l,t[1]),color:i.colorizer.getLabelFill(t)}})}),a}},{key:"_getRawBlockCount",value:function(t){return Array.isArray(t[1])?t[1][0]:t[1]}},{key:"_getDx",value:function(){return this.bottomPinch>0?this.bottomLeftX/(this.blocks.length-this.bottomPinch):this.bottomLeftX/this.blocks.length}},{key:"_getDy",value:function(){return this.isCurved?(this.height-this.curveHeight)/this.blocks.length:this.height/this.blocks.length}},{key:"_draw",value:function(){this.svg=s["default"].select(this.selector).append("svg").attr("width",this.width).attr("height",this.height);var t=this._makePaths();this.blockPaths=t[0],this.overlayPaths=t[1],"gradient"===this.fillType&&this._defineColorGradients(this.svg),this.isCurved&&this._drawTopOval(this.svg,this.blockPaths),this._drawBlock(0)}},{key:"_makePaths",value:function(){var t=this,e=[],i=[],a=this.dx,r=this.dy,o=0,l=this.width,h=0;this.isInverted&&(o=this.bottomLeftX,l=this.width-this.bottomLeftX);var s=0,u=0,c=0,f=this.width/2;this.isCurved&&(h=10);var d=this.height;0!==this.minHeight&&(d=this.height-this.minHeight*this.blocks.length);var v=this.height;this.blocks.forEach(function(e,i){t.bottomPinch>0&&(t.isInverted?i<t.bottomPinch&&(v-=e.height):i>=t.blocks.length-t.bottomPinch&&(v-=e.height))});var y=2*v/(this.width-this.bottomWidth);return this.blocks.forEach(function(v,g){if(t.dynamicHeight&&(r=d*v.ratio,0!==t.minHeight&&(r+=t.minHeight),t.isCurved&&(r-=t.curveHeight/t.blocks.length),s=(h+r)/y,t.isInverted&&(s=(h+r-t.height)/(-1*y)),0===t.bottomWidth&&g===t.blocks.length-1&&(s=t.width/2,t.isInverted&&(s=0)),t.bottomWidth===t.width&&(s=o),a=s-o,t.isInverted&&(a=o-s)),t.dynamicSlope){var p=t.blocks[g+1]?t.blocks[g+1].value:v.value,b=1-p/v.value;a=b*(f-o)}t.bottomPinch>0&&(t.isInverted?(t.dynamicHeight||(a=t.dx),a=g<t.bottomPinch?0:a):g>=t.blocks.length-t.bottomPinch&&(a=0)),s=o+a,u=l-a,c=h+r,t.isInverted&&(s=o-a,u=l+a);var k={centerX:f,prevLeftX:o,prevRightX:l,prevHeight:h,nextLeftX:s,nextRightX:u,nextHeight:c,curveHeight:t.curveHeight,ratio:v.ratio};t.isCurved?(e=[].concat(n(e),[t.navigator.makeCurvedPaths(k)]),t.addValueOverlay&&(i=[].concat(n(i),[t.navigator.makeCurvedPaths(k,!0)]))):(e=[].concat(n(e),[t.navigator.makeStraightPaths(k)]),t.addValueOverlay&&(i=[].concat(n(i),[t.navigator.makeStraightPaths(k,!0)]))),o=s,l=u,h=c}),[e,i]}},{key:"_defineColorGradients",value:function(t){var e=this,i=t.append("defs");this.blocks.forEach(function(t,a){var n=t.fill.raw,r=e.colorizer.shade(n,-.2),o=i.append("linearGradient").attr({id:"gradient-"+a}),l=[[0,r],[40,n],[60,n],[100,r]];l.forEach(function(t){o.append("stop").attr({offset:t[0]+"%",style:"stop-color: "+t[1]})})})}},{key:"_drawTopOval",value:function(t,e){var i=0,a=this.width,n=this.width/2;this.isInverted&&(i=this.bottomLeftX,a=this.width-this.bottomLeftX);var r=e[0],o=r[1][1]+this.curveHeight-10,l=this.navigator.plot([["M",i,r[0][1]],["Q",n,o],[" ",a,r[2][1]],["M",a,10],["Q",n,0],[" ",i,10]]);t.append("path").attr("fill",this.colorizer.shade(this.blocks[0].fill.raw,-.4)).attr("d",l)}},{key:"_drawBlock",value:function(t){var e=this;if(t!==this.blocks.length){var i=this.svg.append("g"),a=this._getBlockPath(i,t);this._attachData(a,this.blocks[t]);var n=null,r=this.blocks[t].fill.actual;this.addValueOverlay&&(n=this._getOverlayPath(i,t),this._attachData(n,this.blocks[t]),a.node().setAttribute("pathType","background"),n.node().setAttribute("pathType","foreground"),r=this.colorizer.shade(this.blocks[t].fill.raw,.3)),0!==this.animation?a.transition().duration(this.animation).ease("linear").attr("fill",r).attr("d",this._getPathDefinition(t)).each("end",function(){e._drawBlock(t+1)}):(a.attr("fill",r).attr("d",this._getPathDefinition(t)),this._drawBlock(t+1)),this.addValueOverlay&&(a.attr("stroke",this.blocks[t].fill.raw),0!==this.animation?n.transition().duration(this.animation).ease("linear").attr("fill",this.blocks[t].fill.actual).attr("d",this._getOverlayPathDefinition(t)):n.attr("fill",this.blocks[t].fill.actual).attr("d",this._getOverlayPathDefinition(t))),this.hoverEffects&&[a,n].forEach(function(t){t&&t.on("mouseover",e._onMouseOver.bind(e)).on("mouseout",e._onMouseOut.bind(e))}),null!==this.onBlockClick&&[a,n].forEach(function(t){t&&t.on("click",e.onBlockClick)}),this._addBlockLabel(i,t)}}},{key:"_getBlockPath",value:function(t,e){var i=t.append("path");return 0!==this.animation&&this._addBeforeTransition(i,e),i}},{key:"_getOverlayPath",value:function(t,e){var i=t.append("path");return 0!==this.animation&&this._addBeforeTransition(i,e,!0),i}},{key:"_addBeforeTransition",value:function(t,e,i){var a=i?this.overlayPaths[e]:this.blockPaths[e],n="",r="";n=this.isCurved?this.navigator.plot([["M",a[0][0],a[0][1]],["Q",a[1][0],a[1][1]],[" ",a[2][0],a[2][1]],["L",a[2][0],a[2][1]],["M",a[2][0],a[2][1]],["Q",a[1][0],a[1][1]],[" ",a[0][0],a[0][1]]]):this.navigator.plot([["M",a[0][0],a[0][1]],["L",a[1][0],a[1][1]],["L",a[1][0],a[1][1]],["L",a[0][0],a[0][1]]]),r="solid"===this.fillType&&e>0?this.blocks[e-1].fill.actual:this.blocks[e].fill.actual,t.attr("d",n).attr("fill",r)}},{key:"_attachData",value:function(t,e){var i=o({},e,{node:t.node()});t.data([i])}},{key:"_getPathDefinition",value:function(t){var e=[];return this.blockPaths[t].forEach(function(t){e.push([t[2],t[0],t[1]])}),this.navigator.plot(e)}},{key:"_getOverlayPathDefinition",value:function(t){var e=[];return this.overlayPaths[t].forEach(function(t){e.push([t[2],t[0],t[1]])}),this.navigator.plot(e)}},{key:"_onMouseOver",value:function(t){for(var e=s["default"].event.target.parentElement.childNodes,i=0;i<e.length;i++){var a=e[i];if("path"===a.nodeName.toLowerCase()){var n=a.getAttribute("pathType")||"";"foreground"===n?s["default"].select(a).attr("fill",this.colorizer.shade(t.fill.raw,-.5)):s["default"].select(a).attr("fill",this.colorizer.shade(t.fill.raw,-.2))}}}},{key:"_onMouseOut",value:function(t){for(var e=s["default"].event.target.parentElement.childNodes,i=0;i<e.length;i++){var a=e[i];if("path"===a.nodeName.toLowerCase()){var n=a.getAttribute("pathType")||"";if("background"===n){var r=this.colorizer.shade(t.fill.raw,.3);s["default"].select(a).attr("fill",r)}else s["default"].select(a).attr("fill",t.fill.actual)}}}},{key:"_addBlockLabel",value:function(t,e){var i=this.blockPaths[e],a=this.blocks[e].label.formatted,n=this.blocks[e].label.color,r=this.width/2,o=this._getTextY(i),l=t.append("text").text(a).attr({x:r,y:o,fill:n,"text-anchor":"middle","dominant-baseline":"middle","pointer-events":"none"}).attr("font-size",this.label.fontSize);null!==this.label.fontFamily&&l.attr("font-family",this.label.fontFamily)}},{key:"_getTextY",value:function(t){return this.isCurved?(t[2][1]+t[3][1])/2+this.curveHeight/this.blocks.length:(t[1][1]+t[2][1])/2}}]),t}();b.defaults={chart:{width:350,height:400,bottomWidth:1/3,bottomPinch:0,inverted:!1,horizontal:!1,animate:0,curve:{enabled:!1,height:20},totalCount:null},block:{dynamicHeight:!1,dynamicSlope:!1,barOverlay:!1,fill:{scale:s["default"].scale.category10().domain(s["default"].range(0,10)),type:"solid"},minHeight:0,highlight:!1},label:{fontFamily:null,fontSize:"14px",fill:"#fff",format:"{l}: {f}"},events:{click:{block:null}}},e["default"]=b},function(e,i){e.exports=t},function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}return function(e,i,a){return i&&t(e.prototype,i),a&&t(e,a),e}}(),n=function(){function t(){i(this,t),this.hexExpression=/^#([0-9a-f]{3}|[0-9a-f]{6})$/i,this.labelFill=null,this.scale=null}return a(t,[{key:"setLabelFill",value:function(t){this.labelFill=t}},{key:"setScale",value:function(t){this.scale=t}},{key:"getBlockFill",value:function(t,e,i){var a=this.getBlockRawFill(t,e);return{raw:a,actual:this.getBlockActualFill(a,e,i)}}},{key:"getBlockRawFill",value:function(t,e){return t.length>2&&this.hexExpression.test(t[2])?t[2]:Array.isArray(this.scale)?this.scale[e]:this.scale(e)}},{key:"getBlockActualFill",value:function(t,e,i){return"solid"===i?t:"url(#gradient-"+e+")"}},{key:"getLabelFill",value:function(t){return t.length>3&&this.hexExpression.test(t[3])?t[3]:this.labelFill}},{key:"shade",value:function(t,e){var i=t.slice(1);3===i.length&&(i=this.expandHex(i));var a=parseInt(i,16),n=0>e?0:255,r=0>e?-1*e:e,o=a>>16,l=a>>8&255,h=255&a,s=16777216+65536*(Math.round((n-o)*r)+o)+256*(Math.round((n-l)*r)+l)+(Math.round((n-h)*r)+h);return"#"+s.toString(16).slice(1)}},{key:"expandHex",value:function(t){return t[0]+t[0]+t[1]+t[1]+t[2]+t[2]}}]),t}();e["default"]=n},function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}return function(e,i,a){return i&&t(e.prototype,i),a&&t(e,a),e}}(),n=function(){function t(){i(this,t),this.expression=null}return a(t,[{key:"setFormat",value:function(t){"function"==typeof t?this.formatter=t:(this.expression=t,this.formatter=this.stringFormatter)}},{key:"format",value:function(t,e){return Array.isArray(e)?this.formatter(t,e[0],e[1]):this.formatter(t,e,null)}},{key:"stringFormatter",value:function(t,e){var i=arguments.length<=2||void 0===arguments[2]?null:arguments[2],a=i;return null===i&&(a=this.getDefaultFormattedValue(e)),this.expression.split("{l}").join(t).split("{v}").join(e).split("{f}").join(a)}},{key:"getDefaultFormattedValue",value:function(t){return t.toLocaleString()}}]),t}();e["default"]=n},function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}return function(e,i,a){return i&&t(e.prototype,i),a&&t(e,a),e}}(),n=function(){function t(){i(this,t)}return a(t,[{key:"plot",value:function(t){var e="";return t.forEach(function(t){e+=""+t[0]+t[1]+","+t[2]+" "}),e.replace(/ +/g," ").trim()}},{key:"makeCurvedPaths",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?!1:arguments[1],i=this.makeBezierPoints(t);return e?this.makeBezierPath(i,t.ratio):this.makeBezierPath(i)}},{key:"makeBezierPoints",value:function(t){var e=t.centerX,i=t.prevLeftX,a=t.prevRightX,n=t.prevHeight,r=t.nextLeftX,o=t.nextRightX,l=t.nextHeight,h=t.curveHeight;return{p00:{x:i,y:n},p01:{x:e,y:n+h-10},p02:{x:a,y:n},p10:{x:r,y:l},p11:{x:e,y:l+h},p12:{x:o,y:l}}}},{key:"makeBezierPath",value:function(t){var e=t.p00,i=t.p01,a=t.p02,n=t.p10,r=t.p11,o=t.p12,l=arguments.length<=1||void 0===arguments[1]?1:arguments[1],h=this.getQuadraticBezierCurve(e,i,a,l),s=this.getQuadraticBezierCurve(n,r,o,l);return[[h.p0.x,h.p0.y,"M"],[h.p1.x,h.p1.y,"Q"],[h.p2.x,h.p2.y,""],[s.p2.x,s.p2.y,"L"],[s.p2.x,s.p2.y,"M"],[s.p1.x,s.p1.y,"Q"],[s.p0.x,s.p0.y,""],[h.p0.x,h.p0.y,"L"]]}},{key:"getQuadraticBezierCurve",value:function(t,e,i){var a=arguments.length<=3||void 0===arguments[3]?1:arguments[3];return{p0:t,p1:{x:this.getLinearInterpolation(t,e,a,"x"),y:this.getLinearInterpolation(t,e,a,"y")},p2:{x:this.getQuadraticInterpolation(t,e,i,a,"x"),y:this.getQuadraticInterpolation(t,e,i,a,"y")}}}},{key:"getLinearInterpolation",value:function(t,e,i,a){return t[a]+i*(e[a]-t[a])}},{key:"getQuadraticInterpolation",value:function(t,e,i,a,n){return Math.pow(1-a,2)*t[n]+2*(1-a)*a*e[n]+Math.pow(a,2)*i[n]}},{key:"makeStraightPaths",value:function(t){var e=t.prevLeftX,i=t.prevRightX,a=t.prevHeight,n=t.nextLeftX,r=t.nextRightX,o=t.nextHeight,l=t.ratio,h=arguments.length<=1||void 0===arguments[1]?!1:arguments[1];if(h){var s=i-e,u=r-n,c=s*(l||0)+e,f=u*(l||0)+n;return c=Math.min(c,s),f=Math.min(f,u),[[e,a,"M"],[c,a,"L"],[f,o,"L"],[n,o,"L"],[e,a,"L"]]}return[[e,a,"M"],[i,a,"L"],[r,o,"L"],[n,o,"L"],[e,a,"L"]]}}]),t}();e["default"]=n},function(t,e){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol?"symbol":typeof t},n=function(){function t(t,e){for(var i=0;i<e.length;i++){var a=e[i];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(t,a.key,a)}}return function(e,i,a){return i&&t(e.prototype,i),a&&t(e,a),e}}(),r=function(){function t(){i(this,t)}return n(t,null,[{key:"isExtendableObject",value:function(t){return"object"===("undefined"==typeof t?"undefined":a(t))&&null!==t&&!Array.isArray(t)}},{key:"extend",value:function(e,i){var a={};return Object.keys(e).length>0&&(a=t.extend({},e)),Object.keys(i).forEach(function(n){t.isExtendableObject(i[n])?t.isExtendableObject(e[n])?a[n]=t.extend(e[n],i[n]):a[n]=t.extend({},i[n]):a[n]=i[n]}),a}}]),t}();e["default"]=r}])}); |
@@ -1,178 +0,188 @@ | ||
$(function() { | ||
$('#picker').change(function() { | ||
var index = $(this).val(); | ||
/* global D3Funnel, $ */ | ||
var data = { | ||
normal: [ | ||
['Applicants', 12000], | ||
['Pre-screened', 4000], | ||
['Interviewed', 2500], | ||
['Hired', 1500], | ||
], | ||
dynamicSlope: [ | ||
['Applicants', 10], | ||
['Pre-screened', 8], | ||
['Interviewed', 6], | ||
['Hired', 2], | ||
], | ||
minHeight: [ | ||
['Applicants', 12000], | ||
['Pre-screened', 4000], | ||
['Interviewed', 2500], | ||
['Hired', 100], | ||
], | ||
color: [ | ||
['Teal', 12000, '#008080'], | ||
['Byzantium', 4000, '#702963'], | ||
['Persimmon', 2500, '#ff634d'], | ||
['Azure', 1500, '#007fff'], | ||
], | ||
labelsColor: [ | ||
['Teal', 12000, '#008080', '#00cdcd'], | ||
['Byzantium', 4000, '#702963', '#c86cb8'], | ||
['Persimmon', 2500, '#ff634d', '#b31600'], | ||
['Azure', 1500, '#007fff', '#003367'], | ||
], | ||
}; | ||
var options = { | ||
basic: [data.normal, {}], | ||
formatted: [ | ||
data.normal, { | ||
label: { | ||
format: '{l}: ${f}', | ||
}, | ||
const data = { | ||
normal: [ | ||
['Applicants', 12000], | ||
['Pre-screened', 4000], | ||
['Interviewed', 2500], | ||
['Hired', 1500], | ||
], | ||
dynamicSlope: [ | ||
['Applicants', 10], | ||
['Pre-screened', 8], | ||
['Interviewed', 6], | ||
['Hired', 2], | ||
], | ||
minHeight: [ | ||
['Applicants', 12000], | ||
['Pre-screened', 4000], | ||
['Interviewed', 2500], | ||
['Hired', 100], | ||
], | ||
color: [ | ||
['Teal', 12000, '#008080'], | ||
['Byzantium', 4000, '#702963'], | ||
['Persimmon', 2500, '#ff634d'], | ||
['Azure', 1500, '#007fff'], | ||
], | ||
labelsColor: [ | ||
['Teal', 12000, '#008080', '#00cdcd'], | ||
['Byzantium', 4000, '#702963', '#c86cb8'], | ||
['Persimmon', 2500, '#ff634d', '#b31600'], | ||
['Azure', 1500, '#007fff', '#003367'], | ||
], | ||
}; | ||
const options = { | ||
basic: [data.normal, {}], | ||
formatted: [ | ||
data.normal, { | ||
label: { | ||
format: '{l}: ${f}', | ||
}, | ||
}, | ||
], | ||
curved: [ | ||
data.normal, { | ||
chart: { | ||
curve: { | ||
enabled: true, | ||
}, | ||
], | ||
curved: [ | ||
data.normal, { | ||
chart: { | ||
curve: { | ||
enabled: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
pinch: [ | ||
data.normal, { | ||
chart: { | ||
bottomPinch: 1, | ||
}, | ||
}, | ||
], | ||
gradient: [ | ||
data.normal, { | ||
block: { | ||
fill: { | ||
type: 'gradient', | ||
}, | ||
], | ||
pinch: [ | ||
data.normal, { | ||
chart: { | ||
bottomPinch: 1, | ||
}, | ||
}, | ||
], | ||
inverted: [ | ||
data.normal, { | ||
chart: { | ||
inverted: true, | ||
}, | ||
}, | ||
], | ||
hover: [ | ||
data.normal, { | ||
block: { | ||
highlight: true, | ||
}, | ||
}, | ||
], | ||
dynamicHeight: [ | ||
data.normal, { | ||
chart: { | ||
bottomWidth: 1 / 3, | ||
}, | ||
block: { | ||
dynamicHeight: true, | ||
}, | ||
}, | ||
], | ||
dynamicSlope: [ | ||
data.dynamicSlope, { | ||
block: { | ||
dynamicSlope: true, | ||
}, | ||
}, | ||
], | ||
minHeight: [ | ||
data.minHeight, { | ||
block: { | ||
dynamicHeight: true, | ||
minHeight: 20, | ||
}, | ||
}, | ||
], | ||
animation: [ | ||
data.normal, { | ||
chart: { | ||
animate: 200, | ||
}, | ||
}, | ||
], | ||
clickEvent: [ | ||
data.normal, { | ||
events: { | ||
click: { | ||
block: (d) => { | ||
alert(`<${d.label.raw}> selected.`); | ||
}, | ||
}, | ||
], | ||
gradient: [ | ||
data.normal, { | ||
block: { | ||
fill: { | ||
type: 'gradient', | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
label: [ | ||
data.normal, { | ||
label: { | ||
fontFamily: 'Open Sans', | ||
fontSize: '16px', | ||
fill: '#000', | ||
}, | ||
}, | ||
], | ||
color: [data.color, {}], | ||
labelsColor: [data.labelsColor, {}], | ||
valueOverlay: [ | ||
data.normal, { | ||
block: { | ||
barOverlay: true, | ||
}, | ||
}, | ||
], | ||
works: [ | ||
data.normal, { | ||
chart: { | ||
bottomPinch: 2, | ||
bottomWidth: 1 / 2, | ||
animate: 200, | ||
curve: { | ||
enabled: false, | ||
}, | ||
], | ||
inverted: [ | ||
data.normal, { | ||
chart: { | ||
inverted: true, | ||
}, | ||
}, | ||
block: { | ||
dynamicHeight: true, | ||
fill: { | ||
type: 'gradient', | ||
}, | ||
], | ||
hover: [ | ||
data.normal, { | ||
block: { | ||
highlight: true, | ||
highlight: true, | ||
}, | ||
events: { | ||
click: { | ||
block: (d) => { | ||
alert(`<${d.label.raw}> selected.`); | ||
}, | ||
}, | ||
], | ||
dynamicHeight: [ | ||
data.normal, { | ||
chart: { | ||
bottomWidth: 1 / 3, | ||
}, | ||
block: { | ||
dynamicHeight: true, | ||
}, | ||
}, | ||
], | ||
dynamicSlope: [ | ||
data.dynamicSlope, { | ||
block: { | ||
dynamicSlope: true, | ||
}, | ||
}, | ||
], | ||
minHeight: [ | ||
data.minHeight, { | ||
block: { | ||
dynamicHeight: true, | ||
minHeight: 20, | ||
}, | ||
}, | ||
], | ||
animation: [ | ||
data.normal, { | ||
chart: { | ||
animate: 200, | ||
}, | ||
}, | ||
], | ||
clickEvent: [ | ||
data.normal, { | ||
events: { | ||
click: { | ||
block: function(d) { | ||
alert('<' + d.label.raw + '> selected.'); | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
label: [ | ||
data.normal, { | ||
label: { | ||
fontFamily: 'Open Sans', | ||
fontSize: '16px', | ||
fill: '#000', | ||
}, | ||
}, | ||
], | ||
color: [data.color, {}], | ||
labelsColor: [data.labelsColor, {}], | ||
works: [ | ||
data.normal, { | ||
chart: { | ||
bottomPinch: 2, | ||
bottomWidth: 1 / 2, | ||
animate: 200, | ||
curve: { | ||
enabled: true, | ||
}, | ||
}, | ||
block : { | ||
dynamicHeight: true, | ||
fill: { | ||
type: 'gradient', | ||
}, | ||
highlight: true, | ||
}, | ||
events: { | ||
click: { | ||
block: function(d) { | ||
alert('<' + d.label.raw + '> selected.'); | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
}, | ||
}, | ||
], | ||
}; | ||
var chart = new D3Funnel('#funnel'); | ||
const chart = new D3Funnel('#funnel'); | ||
const picker = document.getElementById('picker'); | ||
// Reverse the dataset if the isInverted option is present | ||
if (options[index][1].hasOwnProperty('isInverted')) { | ||
chart.draw(options[index][0].reverse(), options[index][1]); | ||
// Otherwise, just use the regular data | ||
} else { | ||
chart.draw(options[index][0], options[index][1]); | ||
} | ||
}).trigger('change'); | ||
picker.addEventListener('change', () => { | ||
const index = picker.value; | ||
// Reverse the dataset if the isInverted option is present | ||
// Otherwise, just use the regular data | ||
if (options[index][1].hasOwnProperty('isInverted')) { | ||
chart.draw(options[index][0].reverse(), options[index][1]); | ||
} else { | ||
chart.draw(options[index][0], options[index][1]); | ||
} | ||
}); | ||
// Trigger change event for initial render | ||
picker.dispatchEvent(new CustomEvent('change')); |
{ | ||
"name": "d3-funnel", | ||
"version": "0.7.6", | ||
"version": "0.7.7", | ||
"description": "A library for rendering SVG funnel charts using D3.js", | ||
@@ -5,0 +5,0 @@ "author": "Jake Zatecky", |
@@ -65,23 +65,25 @@ # D3 Funnel | ||
| Option | Description | Type | Default | | ||
| --------------------- | ------------------------------------------------------------------------- | -------- | ----------------------- | | ||
| `chart.width` | The pixel width of the chart. | number | Container's width | | ||
| `chart.height` | The pixel height of the chart. | number | Container's height | | ||
| `chart.bottomWidth` | The percent of total width the bottom should be. | number | `1 / 3` | | ||
| `chart.bottomPinch` | How many blocks to pinch on the bottom to create a funnel "neck". | number | `0` | | ||
| `chart.inverted` | Whether the funnel direction is inverted (like a pyramid). | bool | `false` | | ||
| `chart.animate` | The load animation speed in milliseconds. | number | `0` (disabled) | | ||
| `chart.curve.enabled` | Whether the funnel is curved. | bool | `false` | | ||
| `chart.curve.height` | The curvature amount. | number | `20` | | ||
| `block.dynamicHeight` | Whether the block heights are proportional to its weight. | bool | `false` | | ||
| `block.dynamicSlope` | Whether the block widths are proportional to their value decrease. | bool | `false` | | ||
| `block.fill.scale` | The background color scale as an array or function. | mixed | `d3.scale.category10()` | | ||
| `block.fill.type` | Either `'solid'` or `'gradient'`. | string | `'solid'` | | ||
| `block.minHeight` | The minimum pixel height of a block. | number | `0` | | ||
| `block.highlight` | Whether the blocks are highlighted on hover. | bool | `false` | | ||
| `label.fontFamily` | Any valid font family for the labels. | string | `null` | | ||
| `label.fontSize` | Any valid font size for the labels. | string | `'14px'` | | ||
| `label.fill` | Any valid hex color for the label color. | string | `'#fff'` | | ||
| `label.format` | Either `function(label, value)`, an array, or a format string. See below. | mixed | `'{l}: {f}'` | | ||
| `events.click.block` | Callback `function(data)` for when a block is clicked. | function | `null` | | ||
| Option | Description | Type | Default | | ||
| ---------------------- | ------------------------------------------------------------------------- | -------- | ----------------------- | | ||
| `chart.width` | The pixel width of the chart. | number | Container's width | | ||
| `chart.height` | The pixel height of the chart. | number | Container's height | | ||
| `chart.bottomWidth` | The percent of total width the bottom should be. | number | `1 / 3` | | ||
| `chart.bottomPinch` | How many blocks to pinch on the bottom to create a funnel "neck". | number | `0` | | ||
| `chart.inverted` | Whether the funnel direction is inverted (like a pyramid). | bool | `false` | | ||
| `chart.animate` | The load animation speed in milliseconds. | number | `0` (disabled) | | ||
| `chart.curve.enabled` | Whether the funnel is curved. | bool | `false` | | ||
| `chart.curve.height` | The curvature amount. | number | `20` | | ||
| `chart.totalCount` | Override the total count used in ratio calculations. | number | `null` | | ||
| `block.dynamicHeight` | Whether the block heights are proportional to their weight. | bool | `false` | | ||
| `block.dynamicSlope` | Whether the block widths are proportional to their value decrease. | bool | `false` | | ||
| `block.barOverlay` | Whether the blocks have bar chart overlays proportional to its weight. | bool | `false` | | ||
| `block.fill.scale` | The background color scale as an array or function. | mixed | `d3.scale.category10()` | | ||
| `block.fill.type` | Either `'solid'` or `'gradient'`. | string | `'solid'` | | ||
| `block.minHeight` | The minimum pixel height of a block. | number | `0` | | ||
| `block.highlight` | Whether the blocks are highlighted on hover. | bool | `false` | | ||
| `label.fontFamily` | Any valid font family for the labels. | string | `null` | | ||
| `label.fontSize` | Any valid font size for the labels. | string | `'14px'` | | ||
| `label.fill` | Any valid hex color for the label color. | string | `'#fff'` | | ||
| `label.format` | Either `function(label, value)`, an array, or a format string. See below. | mixed | `'{l}: {f}'` | | ||
| `events.click.block` | Callback `function(data)` for when a block is clicked. | function | `null` | | ||
@@ -88,0 +90,0 @@ ### Label Format |
@@ -23,2 +23,3 @@ import d3 from 'd3'; | ||
}, | ||
totalCount: null, | ||
}, | ||
@@ -28,2 +29,3 @@ block: { | ||
dynamicSlope: false, | ||
barOverlay: false, | ||
fill: { | ||
@@ -130,2 +132,3 @@ scale: d3.scale.category10().domain(d3.range(0, 10)), | ||
this.isCurved = settings.chart.curve.enabled; | ||
this.addValueOverlay = settings.block.barOverlay; | ||
this.curveHeight = settings.chart.curve.height; | ||
@@ -138,2 +141,3 @@ this.fillType = settings.block.fill.type; | ||
this.animation = settings.chart.animate; | ||
this.totalCount = settings.chart.totalCount; | ||
@@ -222,5 +226,11 @@ // Support for events | ||
* | ||
* @param {Array} data | ||
* | ||
* @return {Number} | ||
*/ | ||
_getTotalCount(data) { | ||
if (this.totalCount !== null) { | ||
return this.totalCount || 0; | ||
} | ||
let total = 0; | ||
@@ -248,3 +258,3 @@ | ||
const count = this._getRawBlockCount(block); | ||
const ratio = count / totalCount; | ||
const ratio = (count / totalCount) || 0; | ||
const label = block[0]; | ||
@@ -316,3 +326,5 @@ | ||
this.blockPaths = this._makePaths(); | ||
const newPaths = this._makePaths(); | ||
this.blockPaths = newPaths[0]; | ||
this.overlayPaths = newPaths[1]; | ||
@@ -337,6 +349,7 @@ // Define color gradients | ||
* | ||
* @return {Array} | ||
* @return {Array, Array} | ||
*/ | ||
_makePaths() { | ||
const paths = []; | ||
let paths = []; | ||
let overlayPaths = []; | ||
@@ -363,3 +376,3 @@ // Initialize velocity | ||
const middle = this.width / 2; | ||
const centerX = this.width / 2; | ||
@@ -460,3 +473,3 @@ // Move down if there is an initial curve | ||
const widthPercent = 1 - (nextBlockValue / block.value); | ||
dx = widthPercent * (middle - prevLeftX); | ||
dx = widthPercent * (centerX - prevLeftX); | ||
} | ||
@@ -498,32 +511,26 @@ | ||
// Plot curved lines | ||
const dimensions = { | ||
centerX, | ||
prevLeftX, | ||
prevRightX, | ||
prevHeight, | ||
nextLeftX, | ||
nextRightX, | ||
nextHeight, | ||
curveHeight: this.curveHeight, | ||
ratio: block.ratio, | ||
}; | ||
if (this.isCurved) { | ||
paths.push([ | ||
// Top Bezier curve | ||
[prevLeftX, prevHeight, 'M'], | ||
[middle, prevHeight + (this.curveHeight - 10), 'Q'], | ||
[prevRightX, prevHeight, ''], | ||
// Right line | ||
[nextRightX, nextHeight, 'L'], | ||
// Bottom Bezier curve | ||
[nextRightX, nextHeight, 'M'], | ||
[middle, nextHeight + this.curveHeight, 'Q'], | ||
[nextLeftX, nextHeight, ''], | ||
// Left line | ||
[prevLeftX, prevHeight, 'L'], | ||
]); | ||
// Plot straight lines | ||
paths = [...paths, this.navigator.makeCurvedPaths(dimensions)]; | ||
if (this.addValueOverlay) { | ||
overlayPaths = [...overlayPaths, this.navigator.makeCurvedPaths(dimensions, true)]; | ||
} | ||
} else { | ||
paths.push([ | ||
// Start position | ||
[prevLeftX, prevHeight, 'M'], | ||
// Move to right | ||
[prevRightX, prevHeight, 'L'], | ||
// Move down | ||
[nextRightX, nextHeight, 'L'], | ||
// Move to left | ||
[nextLeftX, nextHeight, 'L'], | ||
// Wrap back to top | ||
[prevLeftX, prevHeight, 'L'], | ||
]); | ||
paths = [...paths, this.navigator.makeStraightPaths(dimensions)]; | ||
if (this.addValueOverlay) { | ||
overlayPaths = [...overlayPaths, this.navigator.makeStraightPaths(dimensions, true)]; | ||
} | ||
} | ||
@@ -537,3 +544,3 @@ | ||
return paths; | ||
return [paths, overlayPaths]; | ||
} | ||
@@ -638,2 +645,17 @@ | ||
let overlayPath = null; | ||
let pathColor = this.blocks[index].fill.actual; | ||
if (this.addValueOverlay) { | ||
overlayPath = this._getOverlayPath(group, index); | ||
this._attachData(overlayPath, this.blocks[index]); | ||
// Add data attribute to distinguish between paths | ||
path.node().setAttribute('pathType', 'background'); | ||
overlayPath.node().setAttribute('pathType', 'foreground'); | ||
// Default path becomes background of lighter shade | ||
pathColor = this.colorizer.shade(this.blocks[index].fill.raw, 0.3); | ||
} | ||
// Add animation components | ||
@@ -644,3 +666,3 @@ if (this.animation !== 0) { | ||
.ease('linear') | ||
.attr('fill', this.blocks[index].fill.actual) | ||
.attr('fill', pathColor) | ||
.attr('d', this._getPathDefinition(index)) | ||
@@ -651,3 +673,3 @@ .each('end', () => { | ||
} else { | ||
path.attr('fill', this.blocks[index].fill.actual) | ||
path.attr('fill', pathColor) | ||
.attr('d', this._getPathDefinition(index)); | ||
@@ -657,6 +679,28 @@ this._drawBlock(index + 1); | ||
// Add path overlay | ||
if (this.addValueOverlay) { | ||
path.attr('stroke', this.blocks[index].fill.raw); | ||
if (this.animation !== 0) { | ||
overlayPath.transition() | ||
.duration(this.animation) | ||
.ease('linear') | ||
.attr('fill', this.blocks[index].fill.actual) | ||
.attr('d', this._getOverlayPathDefinition(index)); | ||
} else { | ||
overlayPath.attr('fill', this.blocks[index].fill.actual) | ||
.attr('d', this._getOverlayPathDefinition(index)); | ||
} | ||
} | ||
// Add the hover events | ||
if (this.hoverEffects) { | ||
path.on('mouseover', this._onMouseOver.bind(this)) | ||
.on('mouseout', this._onMouseOut.bind(this)); | ||
[path, overlayPath].forEach((target) => { | ||
if (!target) { | ||
return; | ||
} | ||
target.on('mouseover', this._onMouseOver.bind(this)) | ||
.on('mouseout', this._onMouseOut.bind(this)); | ||
}); | ||
} | ||
@@ -666,3 +710,9 @@ | ||
if (this.onBlockClick !== null) { | ||
path.on('click', this.onBlockClick); | ||
[path, overlayPath].forEach((target) => { | ||
if (!target) { | ||
return; | ||
} | ||
target.on('click', this.onBlockClick); | ||
}); | ||
} | ||
@@ -690,11 +740,28 @@ | ||
/** | ||
* @param {Object} group | ||
* @param {int} index | ||
* | ||
* @return {Object} | ||
*/ | ||
_getOverlayPath(group, index) { | ||
const path = group.append('path'); | ||
if (this.animation !== 0) { | ||
this._addBeforeTransition(path, index, true); | ||
} | ||
return path; | ||
} | ||
/** | ||
* Set the attributes of a path element before its animation. | ||
* | ||
* @param {Object} path | ||
* @param {int} index | ||
* @param {Object} path | ||
* @param {int} index | ||
* @param {boolean} isOverlay | ||
* | ||
* @return {void} | ||
*/ | ||
_addBeforeTransition(path, index) { | ||
const paths = this.blockPaths[index]; | ||
_addBeforeTransition(path, index, isOverlay) { | ||
const paths = isOverlay ? this.overlayPaths[index] : this.blockPaths[index]; | ||
@@ -728,3 +795,3 @@ let beforePath = ''; | ||
beforeFill = this.blocks[index - 1].fill.actual; | ||
// Otherwise use current background | ||
// Otherwise use current background | ||
} else { | ||
@@ -772,2 +839,17 @@ beforeFill = this.blocks[index].fill.actual; | ||
/** | ||
* @param {int} index | ||
* | ||
* @return {string} | ||
*/ | ||
_getOverlayPathDefinition(index) { | ||
const commands = []; | ||
this.overlayPaths[index].forEach((command) => { | ||
commands.push([command[2], command[0], command[1]]); | ||
}); | ||
return this.navigator.plot(commands); | ||
} | ||
/** | ||
* @param {Object} data | ||
@@ -778,3 +860,18 @@ * | ||
_onMouseOver(data) { | ||
d3.select(d3.event.target).attr('fill', this.colorizer.shade(data.fill.raw, -0.2)); | ||
const children = d3.event.target.parentElement.childNodes; | ||
for (let i = 0; i < children.length; i++) { | ||
// Highlight all paths within one block | ||
const node = children[i]; | ||
if (node.nodeName.toLowerCase() === 'path') { | ||
const type = node.getAttribute('pathType') || ''; | ||
if (type === 'foreground') { | ||
d3.select(node).attr('fill', this.colorizer.shade(data.fill.raw, -0.5)); | ||
} else { | ||
d3.select(node).attr('fill', this.colorizer.shade(data.fill.raw, -0.2)); | ||
} | ||
} | ||
} | ||
} | ||
@@ -788,3 +885,19 @@ | ||
_onMouseOut(data) { | ||
d3.select(d3.event.target).attr('fill', data.fill.actual); | ||
const children = d3.event.target.parentElement.childNodes; | ||
for (let i = 0; i < children.length; i++) { | ||
// Restore original color for all paths of a block | ||
const node = children[i]; | ||
if (node.nodeName.toLowerCase() === 'path') { | ||
const type = node.getAttribute('pathType') || ''; | ||
if (type === 'background') { | ||
const backgroundColor = this.colorizer.shade(data.fill.raw, 0.3); | ||
d3.select(node).attr('fill', backgroundColor); | ||
} else { | ||
d3.select(node).attr('fill', data.fill.actual); | ||
} | ||
} | ||
} | ||
} | ||
@@ -795,2 +908,3 @@ | ||
* @param {int} index | ||
* | ||
* @return {void} | ||
@@ -797,0 +911,0 @@ */ |
@@ -20,4 +20,222 @@ class Navigator { | ||
/** | ||
* @param {Object} dimensions | ||
* @param {boolean} isValueOverlay | ||
* | ||
* @return {Array} | ||
*/ | ||
makeCurvedPaths(dimensions, isValueOverlay = false) { | ||
const points = this.makeBezierPoints(dimensions); | ||
if (isValueOverlay) { | ||
return this.makeBezierPath(points, dimensions.ratio); | ||
} | ||
return this.makeBezierPath(points); | ||
} | ||
/** | ||
* @param {Number} centerX | ||
* @param {Number} prevLeftX | ||
* @param {Number} prevRightX | ||
* @param {Number} prevHeight | ||
* @param {Number} nextLeftX | ||
* @param {Number} nextRightX | ||
* @param {Number} nextHeight | ||
* @param {Number} curveHeight | ||
* | ||
* @returns {Object} | ||
*/ | ||
makeBezierPoints({ | ||
centerX, | ||
prevLeftX, | ||
prevRightX, | ||
prevHeight, | ||
nextLeftX, | ||
nextRightX, | ||
nextHeight, | ||
curveHeight, | ||
}) { | ||
return { | ||
p00: { | ||
x: prevLeftX, | ||
y: prevHeight, | ||
}, | ||
p01: { | ||
x: centerX, | ||
y: prevHeight + curveHeight - 10, | ||
}, | ||
p02: { | ||
x: prevRightX, | ||
y: prevHeight, | ||
}, | ||
p10: { | ||
x: nextLeftX, | ||
y: nextHeight, | ||
}, | ||
p11: { | ||
x: centerX, | ||
y: nextHeight + curveHeight, | ||
}, | ||
p12: { | ||
x: nextRightX, | ||
y: nextHeight, | ||
}, | ||
}; | ||
} | ||
/** | ||
* @param {Object} p00 | ||
* @param {Object} p01 | ||
* @param {Object} p02 | ||
* @param {Object} p10 | ||
* @param {Object} p11 | ||
* @param {Object} p12 | ||
* @param {Number} ratio | ||
* | ||
* @returns {Array} | ||
*/ | ||
makeBezierPath({ p00, p01, p02, p10, p11, p12 }, ratio = 1) { | ||
const curve0 = this.getQuadraticBezierCurve(p00, p01, p02, ratio); | ||
const curve1 = this.getQuadraticBezierCurve(p10, p11, p12, ratio); | ||
return [ | ||
// Top Bezier curve | ||
[curve0.p0.x, curve0.p0.y, 'M'], | ||
[curve0.p1.x, curve0.p1.y, 'Q'], | ||
[curve0.p2.x, curve0.p2.y, ''], | ||
// Right line | ||
[curve1.p2.x, curve1.p2.y, 'L'], | ||
// Bottom Bezier curve | ||
[curve1.p2.x, curve1.p2.y, 'M'], | ||
[curve1.p1.x, curve1.p1.y, 'Q'], | ||
[curve1.p0.x, curve1.p0.y, ''], | ||
// Left line | ||
[curve0.p0.x, curve0.p0.y, 'L'], | ||
]; | ||
} | ||
/** | ||
* @param {Object} p0 | ||
* @param {Object} p1 | ||
* @param {Object} p2 | ||
* @param {Number} t | ||
* | ||
* @return {Object} | ||
*/ | ||
getQuadraticBezierCurve(p0, p1, p2, t = 1) { | ||
// Quadratic Bezier curve syntax: M(P0) Q(P1) P2 | ||
// Where P0, P2 are the curve endpoints and P1 is the control point | ||
// More generally, at 0 <= t <= 1, we have the following: | ||
// Q0(t), which varies linearly from P0 to P1 | ||
// Q1(t), which varies linearly from P1 to P2 | ||
// B(t), which is interpolated linearly between Q0(t) and Q1(t) | ||
// For an intermediate curve at 0 <= t <= 1: | ||
// P1(t) = Q0(t) | ||
// P2(t) = B(t) | ||
return { | ||
p0, | ||
p1: { | ||
x: this.getLinearInterpolation(p0, p1, t, 'x'), | ||
y: this.getLinearInterpolation(p0, p1, t, 'y'), | ||
}, | ||
p2: { | ||
x: this.getQuadraticInterpolation(p0, p1, p2, t, 'x'), | ||
y: this.getQuadraticInterpolation(p0, p1, p2, t, 'y'), | ||
}, | ||
}; | ||
} | ||
/** | ||
* @param {Object} p0 | ||
* @param {Object} p1 | ||
* @param {Number} t | ||
* @param {string} axis | ||
* | ||
* @return {Number} | ||
*/ | ||
getLinearInterpolation(p0, p1, t, axis) { | ||
return p0[axis] + t * (p1[axis] - p0[axis]); | ||
} | ||
/** | ||
* @param {Object} p0 | ||
* @param {Object} p1 | ||
* @param {Object} p2 | ||
* @param {Number} t | ||
* @param {string} axis | ||
* | ||
* @return {Number} | ||
*/ | ||
getQuadraticInterpolation(p0, p1, p2, t, axis) { | ||
return Math.pow(1 - t, 2) * p0[axis] + | ||
2 * (1 - t) * t * p1[axis] + | ||
Math.pow(t, 2) * p2[axis]; | ||
} | ||
/** | ||
* @param {Number} prevLeftX | ||
* @param {Number} prevRightX | ||
* @param {Number} prevHeight | ||
* @param {Number} nextLeftX | ||
* @param {Number} nextRightX | ||
* @param {Number} nextHeight | ||
* @param {Number} ratio | ||
* @param {boolean} isValueOverlay | ||
* | ||
* @return {Object} | ||
*/ | ||
makeStraightPaths({ | ||
prevLeftX, | ||
prevRightX, | ||
prevHeight, | ||
nextLeftX, | ||
nextRightX, | ||
nextHeight, | ||
ratio, | ||
}, isValueOverlay = false) { | ||
if (isValueOverlay) { | ||
const lengthTop = (prevRightX - prevLeftX); | ||
const lengthBtm = (nextRightX - nextLeftX); | ||
let rightSideTop = lengthTop * (ratio || 0) + prevLeftX; | ||
let rightSideBtm = lengthBtm * (ratio || 0) + nextLeftX; | ||
// Overlay should not be longer than the max length of the path | ||
rightSideTop = Math.min(rightSideTop, lengthTop); | ||
rightSideBtm = Math.min(rightSideBtm, lengthBtm); | ||
return [ | ||
// Start position | ||
[prevLeftX, prevHeight, 'M'], | ||
// Move to right | ||
[rightSideTop, prevHeight, 'L'], | ||
// Move down | ||
[rightSideBtm, nextHeight, 'L'], | ||
// Move to left | ||
[nextLeftX, nextHeight, 'L'], | ||
// Wrap back to top | ||
[prevLeftX, prevHeight, 'L'], | ||
]; | ||
} | ||
return [ | ||
// Start position | ||
[prevLeftX, prevHeight, 'M'], | ||
// Move to right | ||
[prevRightX, prevHeight, 'L'], | ||
// Move down | ||
[nextRightX, nextHeight, 'L'], | ||
// Move to left | ||
[nextLeftX, nextHeight, 'L'], | ||
// Wrap back to top | ||
[prevLeftX, prevHeight, 'L'], | ||
]; | ||
} | ||
} | ||
export default Navigator; |
@@ -539,2 +539,41 @@ /* global beforeEach, describe, it */ | ||
describe('block.barOverlay', () => { | ||
it('should draw value overlay within each path', () => { | ||
getFunnel().draw([ | ||
['A', 10], | ||
['B', 20], | ||
], { | ||
block: { | ||
barOverlay: true, | ||
}, | ||
}); | ||
// draw 2 path for each data point | ||
assert.equal(4, d3.selectAll('#funnel path')[0].length); | ||
}); | ||
it('should draw value overlay with overridden total count', () => { | ||
getFunnel().draw([ | ||
['A', 10], | ||
['B', 20], | ||
], { | ||
chart: { | ||
totalCount: 100, | ||
}, | ||
block: { | ||
barOverlay: true, | ||
}, | ||
}); | ||
const paths = d3.selectAll('path'); | ||
const APathFullWidth = getPathTopWidth(d3.select(paths[0][0])); | ||
const APathOverlayWidth = getPathTopWidth(d3.select(paths[0][1])); | ||
const BPathFullWidth = getPathTopWidth(d3.select(paths[0][2])); | ||
const BPathOverlayWidth = getPathTopWidth(d3.select(paths[0][3])); | ||
assert.equal(10, Math.round(APathOverlayWidth / APathFullWidth * 100)); | ||
assert.equal(20, Math.round(BPathOverlayWidth / BPathFullWidth * 100)); | ||
}); | ||
}); | ||
describe('block.fill.scale', () => { | ||
@@ -541,0 +580,0 @@ it('should use a function\'s return value', () => { |
@@ -1,5 +0,1 @@ | ||
/* eslint-disable */ | ||
var webpack = require('webpack'); | ||
module.exports = { | ||
@@ -6,0 +2,0 @@ output: { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
140675
3694
200