monte
Version:
A visualization framework for D3.js and SVG. Ships with prebuilt charts and components.
132 lines (105 loc) • 4.03 kB
JavaScript
import { ENTER, EXIT, UPDATE } from '../../const/d3';
import { LineChart } from './LineChart';
import { commonEventNames } from '../../tools/commonEventNames';
import { extentFromZero } from '../../util/extents';
import { noop } from '../../tools/noop';
import { resetScaleDomain } from '../../tools/resetScaleDomain';
import { upperFirst } from '../../tools/string';
const AREA = 'area';
const AREA_CHART_DEFAULTS = {
chartCss: 'monte-area-chart',
margin: {
top: 10,
right: 10,
bottom: 30,
left: 40,
},
/***********************************************************************************************
*
* Area related options
*
**********************************************************************************************/
areaProp: '',
// Callback function to customize the area generator.
areaCustomize: null,
// Scale function for the `fill` attribute to apply per area.
areaFillScale: noop,
areaFillScaleAccessor: LineChart.generateScaleAccessor('areaFillScale', 'areaProp'),
// Scale function for CSS class to apply per area. Input: line index, Output: String of CSS Class.
areaCssScale: noop,
areaCssScaleAccessor: LineChart.generateScaleAccessor('areaCssScale', 'areaProp'),
// Static CSS class(es) to apply to every area.
areaCss: 'area',
// OVERRIDES
yDomainCustomize: extentFromZero,
};
export class AreaChart extends LineChart {
_initOptions(...options) {
super._initOptions(...options, AREA_CHART_DEFAULTS);
}
_initCore() {
super._initCore();
// Initialize the line generator
this.area = d3.area()
.x((d) => this.getScaledProp('x', d))
.y0(() => this.height)
.y1((d) => this.getScaledProp('y', d));
}
_initCustomize() {
super._initCustomize();
if (this.opts.areaCustomize) {
this.tryInvoke(this.opts.areaCustomize, this.area);
}
}
_initPublicEvents(...events) {
super._initPublicEvents(...events,
...commonEventNames(AREA) // Area events
);
}
_resetStyleDomains() {
super._resetStyleDomains();
resetScaleDomain(this.opts.areaCssScale);
resetScaleDomain(this.opts.areaFillScale);
}
_update() {
const lineGrps = super._update();
lineGrps.each((d, i, nodes) => {
this._updateLineArea(nodes[i], d, i, nodes);
});
}
_updateLineArea(node, lineDatum, lineIndex, lineNodes) {
const lineGrp = d3.select(node);
// Data join for the area
const area = lineGrp.selectAll('.monte-area').data((d) => [d]);
// Create new area and update existing
const enterAreas = area.enter().insert('path', ':first-child')
.call(this.__bindCommonEvents(AREA));
this._updateLineAreaSelections(enterAreas, ENTER, lineDatum, lineIndex, lineNodes);
this._updateLineAreaSelections(area, UPDATE, lineDatum, lineIndex, lineNodes);
// Fade out removed area
area.exit()
.call((sel) => this.fnInvoke(this.opts.areaExitSelectionCustomize, sel))
.transition()
.call(this._transitionSetup(AREA, EXIT))
.style('opacity', 0)
.call((t) => this.fnInvoke(this.opts.areaExitTransitionCustomize, t))
.remove();
}
_updateLineAreaSelections(sel, stage, lineDatum, lineIndex, lineNodes) {
const selectionFnName = AREA + upperFirst(stage) + 'SelectionCustomize';
const transitionFnName = AREA + upperFirst(stage) + 'TransitionCustomize';
sel.attr('class', (d) => this._buildCss(
['monte-area',
lineDatum.css,
this.opts.lineCssScaleAccessor,
this.opts.areaCss,
this.opts.areaCssScaleAccessor,
d.css], lineDatum, lineIndex))
.call((sel) => this.fnInvoke(this.opts[selectionFnName], sel))
.transition()
.call(this._transitionSetup(AREA, stage), lineDatum, lineIndex, lineNodes)
.attr('d', (d) => this.area(this.getProp('values', d)))
.style('fill', this.optionReaderFunc('areaFillScaleAccessor'))
.call((t) => this.fnInvoke(this.opts[transitionFnName], t));
}
}