c3
Version:
D3-based reusable chart library
301 lines (279 loc) • 8.27 kB
text/typescript
import CLASS from './class'
import { ChartInternal } from './core'
import { isValue, isFunction, isArray, isString, sanitise } from './util'
ChartInternal.prototype.initTooltip = function() {
var $$ = this,
config = $$.config,
i
$$.tooltip = $$.selectChart
.style('position', 'relative')
.append('div')
.attr('class', CLASS.tooltipContainer)
.style('position', 'absolute')
.style('pointer-events', 'none')
.style('display', 'none')
// Show tooltip if needed
if (config.tooltip_init_show) {
if ($$.isTimeSeries() && isString(config.tooltip_init_x)) {
config.tooltip_init_x = $$.parseDate(config.tooltip_init_x)
for (i = 0; i < $$.data.targets[0].values.length; i++) {
if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) {
break
}
}
config.tooltip_init_x = i
}
$$.tooltip.html(
config.tooltip_contents.call(
$$,
$$.data.targets.map(function(d) {
return $$.addName(d.values[config.tooltip_init_x])
}),
$$.axis.getXAxisTickFormat(),
$$.getYFormat($$.hasArcType()),
$$.color
)
)
$$.tooltip
.style('top', config.tooltip_init_position.top)
.style('left', config.tooltip_init_position.left)
.style('display', 'block')
}
}
ChartInternal.prototype.getTooltipSortFunction = function() {
var $$ = this,
config = $$.config
if (config.data_groups.length === 0 || config.tooltip_order !== undefined) {
// if data are not grouped or if an order is specified
// for the tooltip values we sort them by their values
var order = config.tooltip_order
if (order === undefined) {
order = config.data_order
}
var valueOf = function(obj) {
return obj ? obj.value : null
}
// if data are not grouped, we sort them by their value
if (isString(order) && order.toLowerCase() === 'asc') {
return function(a, b) {
return valueOf(a) - valueOf(b)
}
} else if (isString(order) && order.toLowerCase() === 'desc') {
return function(a, b) {
return valueOf(b) - valueOf(a)
}
} else if (isFunction(order)) {
// if the function is from data_order we need
// to wrap the returned function in order to format
// the sorted value to the expected format
var sortFunction = order
if (config.tooltip_order === undefined) {
sortFunction = function(a, b) {
return order(
a
? {
id: a.id,
values: [a]
}
: null,
b
? {
id: b.id,
values: [b]
}
: null
)
}
}
return sortFunction
} else if (isArray(order)) {
return function(a, b) {
return order.indexOf(a.id) - order.indexOf(b.id)
}
}
} else {
// if data are grouped, we follow the order of grouped targets
var ids = $$.orderTargets($$.data.targets).map(function(i) {
return i.id
})
// if it was either asc or desc we need to invert the order
// returned by orderTargets
if ($$.isOrderAsc() || $$.isOrderDesc()) {
ids = ids.reverse()
}
return function(a, b) {
return ids.indexOf(a.id) - ids.indexOf(b.id)
}
}
}
ChartInternal.prototype.getTooltipContent = function(
d,
defaultTitleFormat,
defaultValueFormat,
color
) {
var $$ = this,
config = $$.config,
titleFormat = config.tooltip_format_title || defaultTitleFormat,
nameFormat =
config.tooltip_format_name ||
function(name) {
return name
},
text,
i,
title,
value,
name,
bgcolor
var valueFormat = config.tooltip_format_value
if (!valueFormat) {
valueFormat = $$.isTargetNormalized(d.id)
? (v, ratio) => `${(ratio * 100).toFixed(2)}%`
: defaultValueFormat
}
var tooltipSortFunction = this.getTooltipSortFunction()
if (tooltipSortFunction) {
d.sort(tooltipSortFunction)
}
for (i = 0; i < d.length; i++) {
if (!(d[i] && (d[i].value || d[i].value === 0))) {
continue
}
if ($$.isStanfordGraphType()) {
// Custom tooltip for stanford plots
if (!text) {
title = $$.getStanfordTooltipTitle(d[i])
text = "<table class='" + $$.CLASS.tooltip + "'>" + title
}
bgcolor = $$.getStanfordPointColor(d[i])
name = sanitise(config.data_epochs) // Epochs key name
value = d[i].epochs
} else {
// Regular tooltip
if (!text) {
title = sanitise(titleFormat ? titleFormat(d[i].x, d[i].index) : d[i].x)
text =
"<table class='" +
$$.CLASS.tooltip +
"'>" +
(title || title === 0
? "<tr><th colspan='2'>" + title + '</th></tr>'
: '')
}
value = sanitise(
valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d)
)
if (value !== undefined) {
// Skip elements when their name is set to null
if (d[i].name === null) {
continue
}
name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index))
bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id)
}
}
if (value !== undefined) {
text +=
"<tr class='" +
$$.CLASS.tooltipName +
'-' +
$$.getTargetSelectorSuffix(d[i].id) +
"'>"
text +=
"<td class='name'><span style='background-color:" +
bgcolor +
"'></span>" +
name +
'</td>'
text += "<td class='value'>" + value + '</td>'
text += '</tr>'
}
}
return text + '</table>'
}
ChartInternal.prototype.tooltipPosition = function(
dataToShow,
tWidth,
tHeight,
element
) {
var $$ = this,
config = $$.config,
d3 = $$.d3
var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight
var forArc = $$.hasArcType(),
mouse = d3.mouse(element)
// Determin tooltip position
if (forArc) {
tooltipLeft =
($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0]
tooltipTop =
($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20
} else {
svgLeft = $$.getSvgLeft(true)
if (config.axis_rotated) {
tooltipLeft = svgLeft + mouse[0] + 100
tooltipRight = tooltipLeft + tWidth
chartRight = $$.currentWidth - $$.getCurrentPaddingRight()
tooltipTop = $$.x(dataToShow[0].x) + 20
} else {
tooltipLeft =
svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20
tooltipRight = tooltipLeft + tWidth
chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight()
tooltipTop = mouse[1] + 15
}
if (tooltipRight > chartRight) {
// 20 is needed for Firefox to keep tooltip width
tooltipLeft -= tooltipRight - chartRight + 20
}
if (tooltipTop + tHeight > $$.currentHeight) {
tooltipTop -= tHeight + 30
}
}
if (tooltipTop < 0) {
tooltipTop = 0
}
return {
top: tooltipTop,
left: tooltipLeft
}
}
ChartInternal.prototype.showTooltip = function(selectedData, element) {
var $$ = this,
config = $$.config
var tWidth, tHeight, position
var forArc = $$.hasArcType(),
dataToShow = selectedData.filter(function(d) {
return d && isValue(d.value)
}),
positionFunction =
config.tooltip_position || ChartInternal.prototype.tooltipPosition
if (dataToShow.length === 0 || !config.tooltip_show) {
$$.hideTooltip()
return
}
$$.tooltip
.html(
config.tooltip_contents.call(
$$,
selectedData,
$$.axis.getXAxisTickFormat(),
$$.getYFormat(forArc),
$$.color
)
)
.style('display', 'block')
// Get tooltip dimensions
tWidth = $$.tooltip.property('offsetWidth')
tHeight = $$.tooltip.property('offsetHeight')
position = positionFunction.call(this, dataToShow, tWidth, tHeight, element)
// Set tooltip
$$.tooltip
.style('top', position.top + 'px')
.style('left', position.left + 'px')
}
ChartInternal.prototype.hideTooltip = function() {
this.tooltip.style('display', 'none')
}