c3
Version:
D3-based reusable chart library
394 lines (385 loc) • 11.1 kB
text/typescript
import CLASS from './class'
import { ChartInternal } from './core'
import { isValue } from './util'
ChartInternal.prototype.initGrid = function() {
var $$ = this,
config = $$.config,
d3 = $$.d3
$$.grid = $$.main
.append('g')
.attr('clip-path', $$.clipPathForGrid)
.attr('class', CLASS.grid)
if (config.grid_x_show) {
$$.grid.append('g').attr('class', CLASS.xgrids)
}
if (config.grid_y_show) {
$$.grid.append('g').attr('class', CLASS.ygrids)
}
if (config.grid_focus_show) {
$$.grid
.append('g')
.attr('class', CLASS.xgridFocus)
.append('line')
.attr('class', CLASS.xgridFocus)
}
$$.xgrid = d3.selectAll([])
if (!config.grid_lines_front) {
$$.initGridLines()
}
}
ChartInternal.prototype.initGridLines = function() {
var $$ = this,
d3 = $$.d3
$$.gridLines = $$.main
.append('g')
.attr('clip-path', $$.clipPathForGrid)
.attr('class', CLASS.grid + ' ' + CLASS.gridLines)
$$.gridLines.append('g').attr('class', CLASS.xgridLines)
$$.gridLines.append('g').attr('class', CLASS.ygridLines)
$$.xgridLines = d3.selectAll([])
}
ChartInternal.prototype.updateXGrid = function(withoutUpdate) {
var $$ = this,
config = $$.config,
d3 = $$.d3,
xgridData = $$.generateGridData(config.grid_x_type, $$.x),
tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0
$$.xgridAttr = config.axis_rotated
? {
x1: 0,
x2: $$.width,
y1: function(d) {
return $$.x(d) - tickOffset
},
y2: function(d) {
return $$.x(d) - tickOffset
}
}
: {
x1: function(d) {
return $$.x(d) + tickOffset
},
x2: function(d) {
return $$.x(d) + tickOffset
},
y1: 0,
y2: $$.height
}
$$.xgridAttr.opacity = function() {
var pos = +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1')
return pos === (config.axis_rotated ? $$.height : 0) ? 0 : 1
}
var xgrid = $$.main
.select('.' + CLASS.xgrids)
.selectAll('.' + CLASS.xgrid)
.data(xgridData)
var xgridEnter = xgrid
.enter()
.append('line')
.attr('class', CLASS.xgrid)
.attr('x1', $$.xgridAttr.x1)
.attr('x2', $$.xgridAttr.x2)
.attr('y1', $$.xgridAttr.y1)
.attr('y2', $$.xgridAttr.y2)
.style('opacity', 0)
$$.xgrid = xgridEnter.merge(xgrid)
if (!withoutUpdate) {
$$.xgrid
.attr('x1', $$.xgridAttr.x1)
.attr('x2', $$.xgridAttr.x2)
.attr('y1', $$.xgridAttr.y1)
.attr('y2', $$.xgridAttr.y2)
.style('opacity', $$.xgridAttr.opacity)
}
xgrid.exit().remove()
}
ChartInternal.prototype.updateYGrid = function() {
var $$ = this,
config = $$.config,
gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks)
var ygrid = $$.main
.select('.' + CLASS.ygrids)
.selectAll('.' + CLASS.ygrid)
.data(gridValues)
var ygridEnter = ygrid
.enter()
.append('line')
// TODO: x1, x2, y1, y2, opacity need to be set here maybe
.attr('class', CLASS.ygrid)
$$.ygrid = ygridEnter.merge(ygrid)
$$.ygrid
.attr('x1', config.axis_rotated ? $$.y : 0)
.attr('x2', config.axis_rotated ? $$.y : $$.width)
.attr('y1', config.axis_rotated ? 0 : $$.y)
.attr('y2', config.axis_rotated ? $$.height : $$.y)
ygrid.exit().remove()
$$.smoothLines($$.ygrid, 'grid')
}
ChartInternal.prototype.gridTextAnchor = function(d) {
return d.position ? d.position : 'end'
}
ChartInternal.prototype.gridTextDx = function(d) {
return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4
}
ChartInternal.prototype.xGridTextX = function(d) {
return d.position === 'start'
? -this.height
: d.position === 'middle'
? -this.height / 2
: 0
}
ChartInternal.prototype.yGridTextX = function(d) {
return d.position === 'start'
? 0
: d.position === 'middle'
? this.width / 2
: this.width
}
ChartInternal.prototype.updateGrid = function(duration) {
var $$ = this,
main = $$.main,
config = $$.config,
xgridLine,
xgridLineEnter,
ygridLine,
ygridLineEnter,
xv = $$.xv.bind($$),
yv = $$.yv.bind($$),
xGridTextX = $$.xGridTextX.bind($$),
yGridTextX = $$.yGridTextX.bind($$)
// hide if arc type
$$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible')
main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden')
if (config.grid_x_show) {
$$.updateXGrid()
}
xgridLine = main
.select('.' + CLASS.xgridLines)
.selectAll('.' + CLASS.xgridLine)
.data(config.grid_x_lines)
// enter
xgridLineEnter = xgridLine
.enter()
.append('g')
.attr('class', function(d) {
return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : '')
})
xgridLineEnter
.append('line')
.attr('x1', config.axis_rotated ? 0 : xv)
.attr('x2', config.axis_rotated ? $$.width : xv)
.attr('y1', config.axis_rotated ? xv : 0)
.attr('y2', config.axis_rotated ? xv : $$.height)
.style('opacity', 0)
xgridLineEnter
.append('text')
.attr('text-anchor', $$.gridTextAnchor)
.attr('transform', config.axis_rotated ? '' : 'rotate(-90)')
.attr('x', config.axis_rotated ? yGridTextX : xGridTextX)
.attr('y', xv)
.attr('dx', $$.gridTextDx)
.attr('dy', -5)
.style('opacity', 0)
// udpate
$$.xgridLines = xgridLineEnter.merge(xgridLine)
// done in d3.transition() of the end of this function
// exit
xgridLine
.exit()
.transition()
.duration(duration)
.style('opacity', 0)
.remove()
// Y-Grid
if (config.grid_y_show) {
$$.updateYGrid()
}
ygridLine = main
.select('.' + CLASS.ygridLines)
.selectAll('.' + CLASS.ygridLine)
.data(config.grid_y_lines)
// enter
ygridLineEnter = ygridLine
.enter()
.append('g')
.attr('class', function(d) {
return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : '')
})
ygridLineEnter
.append('line')
.attr('x1', config.axis_rotated ? yv : 0)
.attr('x2', config.axis_rotated ? yv : $$.width)
.attr('y1', config.axis_rotated ? 0 : yv)
.attr('y2', config.axis_rotated ? $$.height : yv)
.style('opacity', 0)
ygridLineEnter
.append('text')
.attr('text-anchor', $$.gridTextAnchor)
.attr('transform', config.axis_rotated ? 'rotate(-90)' : '')
.attr('x', config.axis_rotated ? xGridTextX : yGridTextX)
.attr('y', yv)
.attr('dx', $$.gridTextDx)
.attr('dy', -5)
.style('opacity', 0)
// update
$$.ygridLines = ygridLineEnter.merge(ygridLine)
$$.ygridLines
.select('line')
.transition()
.duration(duration)
.attr('x1', config.axis_rotated ? yv : 0)
.attr('x2', config.axis_rotated ? yv : $$.width)
.attr('y1', config.axis_rotated ? 0 : yv)
.attr('y2', config.axis_rotated ? $$.height : yv)
.style('opacity', 1)
$$.ygridLines
.select('text')
.transition()
.duration(duration)
.attr(
'x',
config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$)
)
.attr('y', yv)
.text(function(d) {
return d.text
})
.style('opacity', 1)
// exit
ygridLine
.exit()
.transition()
.duration(duration)
.style('opacity', 0)
.remove()
}
ChartInternal.prototype.redrawGrid = function(withTransition, transition) {
var $$ = this,
config = $$.config,
xv = $$.xv.bind($$),
lines = $$.xgridLines.select('line'),
texts = $$.xgridLines.select('text')
return [
(withTransition ? lines.transition(transition) : lines)
.attr('x1', config.axis_rotated ? 0 : xv)
.attr('x2', config.axis_rotated ? $$.width : xv)
.attr('y1', config.axis_rotated ? xv : 0)
.attr('y2', config.axis_rotated ? xv : $$.height)
.style('opacity', 1),
(withTransition ? texts.transition(transition) : texts)
.attr(
'x',
config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$)
)
.attr('y', xv)
.text(function(d) {
return d.text
})
.style('opacity', 1)
]
}
ChartInternal.prototype.showXGridFocus = function(selectedData) {
var $$ = this,
config = $$.config,
dataToShow = selectedData.filter(function(d) {
return d && isValue(d.value)
}),
focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus),
xx = $$.xx.bind($$)
if (!config.tooltip_show) {
return
}
// Hide when stanford plot exists
if ($$.hasType('stanford') || $$.hasArcType()) {
return
}
focusEl
.style('visibility', 'visible')
.data([dataToShow[0]])
.attr(config.axis_rotated ? 'y1' : 'x1', xx)
.attr(config.axis_rotated ? 'y2' : 'x2', xx)
$$.smoothLines(focusEl, 'grid')
}
ChartInternal.prototype.hideXGridFocus = function() {
this.main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden')
}
ChartInternal.prototype.updateXgridFocus = function() {
var $$ = this,
config = $$.config
$$.main
.select('line.' + CLASS.xgridFocus)
.attr('x1', config.axis_rotated ? 0 : -10)
.attr('x2', config.axis_rotated ? $$.width : -10)
.attr('y1', config.axis_rotated ? -10 : 0)
.attr('y2', config.axis_rotated ? -10 : $$.height)
}
ChartInternal.prototype.generateGridData = function(type, scale) {
var $$ = this,
gridData = [],
xDomain,
firstYear,
lastYear,
i,
tickNum = $$.main
.select('.' + CLASS.axisX)
.selectAll('.tick')
.size()
if (type === 'year') {
xDomain = $$.getXDomain()
firstYear = xDomain[0].getFullYear()
lastYear = xDomain[1].getFullYear()
for (i = firstYear; i <= lastYear; i++) {
gridData.push(new Date(i + '-01-01 00:00:00'))
}
} else {
gridData = scale.ticks(10)
if (gridData.length > tickNum) {
// use only int
gridData = gridData.filter(function(d) {
return ('' + d).indexOf('.') < 0
})
}
}
return gridData
}
ChartInternal.prototype.getGridFilterToRemove = function(params) {
return params
? function(line) {
var found = false
;[].concat(params).forEach(function(param) {
if (
('value' in param && line.value === param.value) ||
('class' in param && line['class'] === param['class'])
) {
found = true
}
})
return found
}
: function() {
return true
}
}
ChartInternal.prototype.removeGridLines = function(params, forX) {
var $$ = this,
config = $$.config,
toRemove = $$.getGridFilterToRemove(params),
toShow = function(line) {
return !toRemove(line)
},
classLines = forX ? CLASS.xgridLines : CLASS.ygridLines,
classLine = forX ? CLASS.xgridLine : CLASS.ygridLine
$$.main
.select('.' + classLines)
.selectAll('.' + classLine)
.filter(toRemove)
.transition()
.duration(config.transition_duration)
.style('opacity', 0)
.remove()
if (forX) {
config.grid_x_lines = config.grid_x_lines.filter(toShow)
} else {
config.grid_y_lines = config.grid_y_lines.filter(toShow)
}
}