import CLASS from './class' import { ChartInternal } from './core' import { isFunction } from './util' ChartInternal.prototype.initBrush = function(scale) { var $$ = this, d3 = $$.d3 // TODO: dynamically change brushY/brushX according to axis_rotated. $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()) .on('brush', function() { var event = d3.event.sourceEvent if (event && event.type === 'zoom') { return } $$.redrawForBrush() }) .on('end', function() { var event = d3.event.sourceEvent if (event && event.type === 'zoom') { return } if ($$.brush.empty() && event && event.type !== 'end') { $$.brush.clear() } }) $$.brush.updateExtent = function() { var range = this.scale.range(), extent if ($$.config.axis_rotated) { extent = [ [0, range[0]], [$$.width2, range[1]] ] } else { extent = [ [range[0], 0], [range[1], $$.height2] ] } this.extent(extent) return this } $$.brush.updateScale = function(scale) { this.scale = scale return this } $$.brush.update = function(scale) { this.updateScale(scale || $$.subX).updateExtent() $$.context.select('.' + CLASS.brush).call(this) } $$.brush.clear = function() { $$.context.select('.' + CLASS.brush).call($$.brush.move, null) } $$.brush.selection = function() { return d3.brushSelection($$.context.select('.' + CLASS.brush).node()) } $$.brush.selectionAsValue = function(selectionAsValue, withTransition) { var selection, brush if (selectionAsValue) { if ($$.context) { selection = [ this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1]) ] brush = $$.context.select('.' + CLASS.brush) if (withTransition) { brush = brush.transition() } $$.brush.move(brush, selection) } return [] } selection = $$.brush.selection() || [0, 0] return [this.scale.invert(selection[0]), this.scale.invert(selection[1])] } $$.brush.empty = function() { var selection = $$.brush.selection() return !selection || selection[0] === selection[1] } return $$.brush.updateScale(scale) } ChartInternal.prototype.initSubchart = function() { var $$ = this, config = $$.config, context = ($$.context = $$.svg .append('g') .attr('transform', $$.getTranslate('context'))) // set style context.style('visibility', 'visible') // Define g for chart area context .append('g') .attr('clip-path', $$.clipPathForSubchart) .attr('class', CLASS.chart) // Define g for bar chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars) // Define g for line chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines) // Add extent rect for Brush context .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.brush) // ATTENTION: This must be called AFTER chart added // Add Axis $$.axes.subx = context .append('g') .attr('class', CLASS.axisX) .attr('transform', $$.getTranslate('subx')) .attr('clip-path', config.axis_rotated ? '' : $$.clipPathForXAxis) .style('visibility', config.subchart_axis_x_show ? 'visible' : 'hidden'); } ChartInternal.prototype.initSubchartBrush = function() { var $$ = this // Add extent rect for Brush $$.initBrush($$.subX).updateExtent() $$.context.select('.' + CLASS.brush).call($$.brush) } ChartInternal.prototype.updateTargetsForSubchart = function(targets) { var $$ = this, context = $$.context, config = $$.config, contextLineEnter, contextLine, contextBarEnter, contextBar, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$) //-- Bar --// contextBar = context .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets) contextBarEnter = contextBar .enter() .append('g') .style('opacity', 0) contextBarEnter.merge(contextBar).attr('class', classChartBar) // Bars for each data contextBarEnter.append('g').attr('class', classBars) //-- Line --// contextLine = context .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets) contextLineEnter = contextLine .enter() .append('g') .style('opacity', 0) contextLineEnter.merge(contextLine).attr('class', classChartLine) // Lines for each data contextLineEnter.append('g').attr('class', classLines) // Area contextLineEnter.append('g').attr('class', classAreas) //-- Brush --// context .selectAll('.' + CLASS.brush + ' rect') .attr( config.axis_rotated ? 'width' : 'height', config.axis_rotated ? $$.width2 : $$.height2 ) } ChartInternal.prototype.updateBarForSubchart = function(durationForExit) { var $$ = this var contextBar = $$.context .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data($$.barData.bind($$)) var contextBarEnter = contextBar .enter() .append('path') .attr('class', $$.classBar.bind($$)) .style('stroke', 'none') .style('fill', $$.color) contextBar .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove() $$.contextBar = contextBarEnter .merge(contextBar) .style('opacity', $$.initialOpacity.bind($$)) } ChartInternal.prototype.redrawBarForSubchart = function( drawBarOnSub, withTransition, duration ) { ;(withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar ) .attr('d', drawBarOnSub) .style('opacity', 1) } ChartInternal.prototype.updateLineForSubchart = function(durationForExit) { var $$ = this var contextLine = $$.context .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)) var contextLineEnter = contextLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color) contextLine .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove() $$.contextLine = contextLineEnter .merge(contextLine) .style('opacity', $$.initialOpacity.bind($$)) } ChartInternal.prototype.redrawLineForSubchart = function( drawLineOnSub, withTransition, duration ) { ;(withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine ) .attr('d', drawLineOnSub) .style('opacity', 1) } ChartInternal.prototype.updateAreaForSubchart = function(durationForExit) { var $$ = this, d3 = $$.d3 var contextArea = $$.context .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)) var contextAreaEnter = contextArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function() { $$.orgAreaOpacity = +d3.select(this).style('opacity') return 0 }) contextArea .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove() $$.contextArea = contextAreaEnter.merge(contextArea).style('opacity', 0) } ChartInternal.prototype.redrawAreaForSubchart = function( drawAreaOnSub, withTransition, duration ) { ;(withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea ) .attr('d', drawAreaOnSub) .style('fill', this.color) .style('opacity', this.orgAreaOpacity) } ChartInternal.prototype.redrawSubchart = function( withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices ) { var $$ = this, d3 = $$.d3, drawAreaOnSub, drawBarOnSub, drawLineOnSub // reflect main chart to extent on subchart if zoomed if (d3.event && d3.event.type === 'zoom') { $$.brush.selectionAsValue($$.x.orgDomain()) } // update subchart elements if needed if (withSubchart) { // extent rect if (!$$.brush.empty()) { $$.brush.selectionAsValue($$.x.orgDomain()) } // setup drawer - MEMO: this must be called after axis updated drawAreaOnSub = $$.generateDrawArea(areaIndices, true) drawBarOnSub = $$.generateDrawBar(barIndices, true) drawLineOnSub = $$.generateDrawLine(lineIndices, true) $$.updateBarForSubchart(duration) $$.updateLineForSubchart(duration) $$.updateAreaForSubchart(duration) $$.redrawBarForSubchart(drawBarOnSub, duration, duration) $$.redrawLineForSubchart(drawLineOnSub, duration, duration) $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration) } } ChartInternal.prototype.redrawForBrush = function() { var $$ = this, x = $$.x, d3 = $$.d3, s $$.redraw({ withTransition: false, withY: $$.config.zoom_rescale, withSubchart: false, withUpdateXDomain: true, withEventRect: false, withDimension: false }) // update zoom transation binded to event rect s = d3.event.selection || $$.brush.scale.range() $$.main .select('.' + CLASS.eventRect) .call( $$.zoom.transform, d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0) ) $$.config.subchart_onbrush.call($$.api, x.orgDomain()) } ChartInternal.prototype.transformContext = function( withTransition, transitions ) { var $$ = this, subXAxis if (transitions && transitions.axisSubX) { subXAxis = transitions.axisSubX } else { subXAxis = $$.context.select('.' + CLASS.axisX) if (withTransition) { subXAxis = subXAxis.transition() } } $$.context.attr('transform', $$.getTranslate('context')) subXAxis.attr('transform', $$.getTranslate('subx')) } ChartInternal.prototype.getDefaultSelection = function() { var $$ = this, config = $$.config, selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection if ($$.isTimeSeries()) { selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])] } return selection } ChartInternal.prototype.removeSubchart = function() { const $$ = this $$.brush = null $$.context.remove() $$.context = null }