import {BubbleMixin} from '../base/bubble-mixin';
import {CoordinateGridMixin} from '../base/coordinate-grid-mixin';
import {transition} from '../core/core';
import {d3compat} from '../core/config';
/**
* A concrete implementation of a general purpose bubble chart that allows data visualization using the
* following dimensions:
* - x axis position
* - y axis position
* - bubble radius
* - color
*
* Examples:
* - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index}
* - {@link http://dc-js.github.com/dc.js/vc/index.html US Venture Capital Landscape 2011}
* @mixes BubbleMixin
* @mixes CoordinateGridMixin
*/
export class BubbleChart extends BubbleMixin(CoordinateGridMixin) {
/**
* Create a Bubble Chart.
*
* @example
* // create a bubble chart under #chart-container1 element using the default global chart group
* var bubbleChart1 = new BubbleChart('#chart-container1');
* // create a bubble chart under #chart-container2 element using chart group A
* var bubbleChart2 = new BubbleChart('#chart-container2', 'chartGroupA');
* @param {String|node|d3.selection} parent - Any valid
* {@link https://github.com/d3/d3-selection/blob/master/README.md#select d3 single selector} specifying
* a dom block element such as a div; or a dom element or d3 selection.
* @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in.
* Interaction with a chart will only trigger events and redraws within the chart's group.
*/
constructor (parent, chartGroup) {
super();
this.transitionDuration(750);
this.transitionDelay(0);
this.anchor(parent, chartGroup);
}
_bubbleLocator (d) {
return `translate(${this._bubbleX(d)},${this._bubbleY(d)})`;
}
plotData () {
this.calculateRadiusDomain();
this.r().range([this.MIN_RADIUS, this.xAxisLength() * this.maxBubbleRelativeSize()]);
const data = this.data();
let bubbleG = this.chartBodyG().selectAll(`g.${this.BUBBLE_NODE_CLASS}`)
.data(data, d => d.key);
if (this.sortBubbleSize() || this.keyboardAccessible()) {
// update dom order based on sort
bubbleG.order();
}
this._removeNodes(bubbleG);
bubbleG = this._renderNodes(bubbleG);
this._updateNodes(bubbleG);
this.fadeDeselectedArea(this.filter());
}
_renderNodes (bubbleG) {
const bubbleGEnter = bubbleG.enter().append('g');
bubbleGEnter
.attr('class', this.BUBBLE_NODE_CLASS)
.attr('transform', d => this._bubbleLocator(d))
.append('circle').attr('class', (d, i) => `${this.BUBBLE_CLASS} _${i}`)
.on('click', d3compat.eventHandler(d => this.onClick(d)))
.classed('dc-tabbable', this._keyboardAccessible)
.attr('fill', this.getColor)
.attr('r', 0);
bubbleG = bubbleGEnter.merge(bubbleG);
transition(bubbleG, this.transitionDuration(), this.transitionDelay())
.select(`circle.${this.BUBBLE_CLASS}`)
.attr('r', d => this.bubbleR(d))
.attr('opacity', d => (this.bubbleR(d) > 0) ? 1 : 0);
if (this._keyboardAccessible) {
this._makeKeyboardAccessible(this.onClick);
}
this._doRenderLabel(bubbleGEnter);
this._doRenderTitles(bubbleGEnter);
return bubbleG;
}
_updateNodes (bubbleG) {
transition(bubbleG, this.transitionDuration(), this.transitionDelay())
.attr('transform', d => this._bubbleLocator(d))
.select(`circle.${this.BUBBLE_CLASS}`)
.attr('fill', this.getColor)
.attr('r', d => this.bubbleR(d))
.attr('opacity', d => (this.bubbleR(d) > 0) ? 1 : 0);
this.doUpdateLabels(bubbleG);
this.doUpdateTitles(bubbleG);
}
_removeNodes (bubbleG) {
bubbleG.exit().remove();
}
_bubbleX (d) {
let x = this.x()(this.keyAccessor()(d));
if (isNaN(x) || !isFinite(x)) {
x = 0;
}
return x;
}
_bubbleY (d) {
let y = this.y()(this.valueAccessor()(d));
if (isNaN(y) || !isFinite(y)) {
y = 0;
}
return y;
}
renderBrush () {
// override default x axis brush from parent chart
}
redrawBrush (brushSelection, doTransition) {
// override default x axis brush from parent chart
this.fadeDeselectedArea(brushSelection);
}
}
export const bubbleChart = (parent, chartGroup) => new BubbleChart(parent, chartGroup);