Clean up gas chart code.

feature/default_network_editable
Dan Miller 6 years ago
parent aa798cc545
commit 6f0406125d
  1. 11
      ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js
  2. 417
      ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js
  3. 307
      ui/app/components/gas-customization/gas-price-chart/gas-price-chart.utils.js

@ -69,6 +69,10 @@ const mapStateToProps = state => {
const customGasPrice = calcCustomGasPrice(customModalGasPriceInHex) const customGasPrice = calcCustomGasPrice(customModalGasPriceInHex)
const priceAndTimeEstimates = state.gas.priceAndTimeEstimates
const gasPrices = priceAndTimeEstimates.map(({ gasprice }) => gasprice)
const estimatedTimes = priceAndTimeEstimates.map(({ expectedTime }) => expectedTime)
return { return {
hideBasic, hideBasic,
isConfirm: isConfirm(state), isConfirm: isConfirm(state),
@ -77,7 +81,7 @@ const mapStateToProps = state => {
customGasPrice, customGasPrice,
customGasLimit: calcCustomGasLimit(customModalGasLimitInHex), customGasLimit: calcCustomGasLimit(customModalGasLimitInHex),
newTotalFiat, newTotalFiat,
currentTimeEstimate: getRenderableTimeEstimate(customGasPrice, state.gas.priceAndTimeEstimates), currentTimeEstimate: getRenderableTimeEstimate(customGasPrice, priceAndTimeEstimates),
gasPriceButtonGroupProps: { gasPriceButtonGroupProps: {
buttonDataLoading, buttonDataLoading,
defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customModalGasPriceInHex), defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customModalGasPriceInHex),
@ -85,7 +89,10 @@ const mapStateToProps = state => {
}, },
gasChartProps: { gasChartProps: {
currentPrice: customGasPrice, currentPrice: customGasPrice,
priceAndTimeEstimates: state.gas.priceAndTimeEstimates, gasPrices,
estimatedTimes,
gasPricesMax: gasPrices[gasPrices.length - 1] + 1,
estimatedTimesMax: estimatedTimes[0],
}, },
infoRowProps: { infoRowProps: {
originalTotalFiat: addHexWEIsToRenderableFiat(value, gasTotal, currentCurrency, conversionRate), originalTotalFiat: addHexWEIsToRenderableFiat(value, gasTotal, currentCurrency, conversionRate),

@ -2,75 +2,18 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import * as d3 from 'd3' import * as d3 from 'd3'
import c3 from 'c3' import c3 from 'c3'
import {
function setTickPosition (axis, n, newPosition, secondNewPosition) { appendOrUpdateCircle,
const positionToShift = axis === 'y' ? 'x' : 'y' generateChart,
const secondPositionToShift = axis === 'y' ? 'y' : 'x' generateDataUIObj,
d3.select('#chart') getAdjacentGasPrices,
.select(`.c3-axis-${axis}`) getCoordinateData,
.selectAll('.tick') getNewXandTimeEstimate,
.filter((d, i) => i === n) handleChartUpdate,
.select('text') hideDataUI,
.attr(positionToShift, 0) setSelectedCircle,
.select('tspan') setTickPosition,
.attr(positionToShift, newPosition) } from './gas-price-chart.utils.js'
.attr(secondPositionToShift, secondNewPosition || 0)
.style('visibility', 'visible')
}
function appendOrUpdateCircle ({ circle, data, itemIndex, cx, cy, cssId, appendOnly }) {
if (appendOnly || circle.empty()) {
circle.data([data])
.enter().append('circle')
.attr('class', () => this.generateClass('c3-selected-circle', itemIndex))
.attr('id', cssId)
.attr('cx', cx)
.attr('cy', cy)
.attr('stroke', () => this.color(data))
.attr('r', 6)
} else {
circle.data([data])
.attr('cx', cx)
.attr('cy', cy)
}
}
function setSelectedCircle ({ chart, gasPrices, currentPrice, chartXStart, chartWidth }) {
const numberOfValues = chart.internal.data.xs.data1.length
const closestLowerValueIndex = gasPrices.findIndex((e, i, a) => {
return e <= currentPrice && a[i + 1] >= currentPrice
})
const closestHigherValueIndex = gasPrices.findIndex((e, i, a) => {
return e > currentPrice
})
const closestHigherValue = gasPrices[closestHigherValueIndex]
const closestLowerValue = gasPrices[closestLowerValueIndex]
if (closestHigherValue && closestLowerValue) {
const closestLowerCircle = d3.select(`.c3-circle-${closestLowerValueIndex}`)
const closestHigherCircle = d3.select(`.c3-circle-${closestHigherValueIndex}`)
const { x: lowerX, y: lowerY } = closestLowerCircle.node().getBoundingClientRect()
const { x: higherX, y: higherY } = closestHigherCircle.node().getBoundingClientRect()
const currentX = lowerX + (higherX - lowerX) * (currentPrice - closestLowerValue) / (closestHigherValue - closestLowerValue)
const slope = (higherY - lowerY) / (higherX - lowerX)
const newTimeEstimate = -1 * (slope * (higherX - currentX) - higherY)
chart.internal.selectPointB({
x: currentX,
value: newTimeEstimate,
id: 'data1',
index: numberOfValues,
name: 'data1',
}, numberOfValues)
} else {
const setCircle = d3.select('#set-circle')
if (!setCircle.empty()) {
setCircle.remove()
}
d3.select('.c3-tooltip-container').style('display', 'none !important')
chart.internal.hideXGridFocus()
return
}
}
export default class GasPriceChart extends Component { export default class GasPriceChart extends Component {
static contextTypes = { static contextTypes = {
@ -78,225 +21,23 @@ export default class GasPriceChart extends Component {
} }
static propTypes = { static propTypes = {
priceAndTimeEstimates: PropTypes.array, gasPrices: PropTypes.array,
estimatedTimes: PropTypes.array,
gasPricesMax: PropTypes.number,
estimatedTimesMax: PropTypes.number,
currentPrice: PropTypes.number, currentPrice: PropTypes.number,
updateCustomGasPrice: PropTypes.func, updateCustomGasPrice: PropTypes.func,
} }
renderChart (currentPrice, priceAndTimeEstimates, updateCustomGasPrice) { renderChart ({
const gasPrices = priceAndTimeEstimates.map(({ gasprice }) => gasprice) currentPrice,
const gasPricesMax = gasPrices[gasPrices.length - 1] + 1 gasPrices,
const estimatedTimes = priceAndTimeEstimates.map(({ expectedTime }) => expectedTime) estimatedTimes,
gasPricesMax,
const estimatedTimesMax = estimatedTimes[0] estimatedTimesMax,
const chart = c3.generate({ updateCustomGasPrice,
size: { }) {
height: 165, const chart = generateChart(gasPrices, estimatedTimes, gasPricesMax, estimatedTimesMax)
},
transition: {
duration: 0,
},
padding: {left: 20, right: 15, top: 6, bottom: 10},
data: {
x: 'x',
columns: [
['x', ...gasPrices],
['data1', ...estimatedTimes],
],
types: {
data1: 'area',
},
selection: {
enabled: false,
},
},
color: {
data1: '#259de5',
},
axis: {
x: {
min: gasPrices[0],
max: gasPricesMax,
tick: {
values: [Math.floor(gasPrices[0]), Math.ceil(gasPricesMax)],
outer: false,
format: function (val) { return val + ' GWEI' },
},
padding: {left: gasPricesMax / 50, right: gasPricesMax / 50},
label: {
text: 'Gas Price ($)',
position: 'outer-center',
},
},
y: {
padding: {top: 7, bottom: 7},
tick: {
values: [Math.floor(estimatedTimesMax * 0.05), Math.ceil(estimatedTimesMax * 0.97)],
outer: false,
},
label: {
text: 'Confirmation time (sec)',
position: 'outer-middle',
},
min: 0,
},
},
legend: {
show: false,
},
grid: {
x: {},
lines: {
front: false,
},
},
point: {
focus: {
expand: {
enabled: false,
r: 3.5,
},
},
},
tooltip: {
format: {
title: (v) => v.toPrecision(4),
},
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
const config = this.config
const titleFormat = config.tooltip_format_title || defaultTitleFormat
let text
let title
d.forEach(el => {
if (el && (el.value || el.value === 0) && !text) {
title = titleFormat ? titleFormat(el.x) : el.x
text = "<table class='" + 'custom-tooltip' + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + '</th></tr>' : '')
}
})
return text + '</table>' + "<div class='tooltip-arrow'></div>"
},
position: function (data, width, height, element) {
const overlayedCircle = d3.select('#overlayed-circle')
if (overlayedCircle.empty()) {
return { top: -100, left: -100 }
}
const { x: circleX, y: circleY, width: circleWidth } = overlayedCircle.node().getBoundingClientRect()
const { x: chartXStart, y: chartYStart } = d3.select('.c3-chart').node().getBoundingClientRect()
// TODO: Confirm the below constants work with all data sets and screen sizes
// TODO: simplify l149-l159
let y = circleY - chartYStart - 19
if (circleY - circleWidth < chartYStart + 5) {
y = y + circleWidth + 38
d3.select('.tooltip-arrow').style('margin-top', '-16px')
} else {
d3.select('.tooltip-arrow').style('margin-top', '4px')
}
return {
top: y,
left: circleX - chartXStart + circleWidth - (gasPricesMax / 50),
}
},
show: true,
},
})
chart.internal.selectPoint = function (data, itemIndex = (data.index || 0)) {
const { x: circleX, y: circleY, width: circleWidth } = d3.select('#overlayed-circle')
.node()
.getBoundingClientRect()
const { x: chartXStart, y: chartYStart } = d3.select('.c3-areas-data1')
.node()
.getBoundingClientRect()
d3.select('#set-circle').remove()
const circle = this.main
.select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id))
.selectAll('.' + 'c3-selected-circle' + '-' + itemIndex)
appendOrUpdateCircle.bind(this)({
circle,
data,
itemIndex,
cx: () => circleX - chartXStart + circleWidth + 2,
cy: () => circleY - chartYStart + circleWidth + 1,
cssId: 'set-circle',
appendOnly: true,
})
}
chart.internal.selectPointB = function (data, itemIndex = (data.index || 0)) {
const { x: chartXStart, y: chartYStart } = d3.select('.c3-areas-data1')
.node()
.getBoundingClientRect()
d3.select('#set-circle').remove()
const circle = this.main
.select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id))
.selectAll('.' + 'c3-selected-circle' + '-' + itemIndex)
appendOrUpdateCircle.bind(this)({
circle,
data,
itemIndex,
cx: () => data.x - chartXStart + 11,
cy: () => data.value - chartYStart + 10,
cssId: 'set-circle',
appendOnly: true,
})
}
chart.internal.overlayPoint = function (data, itemIndex) {
const circle = this.main
.select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id))
.selectAll('.' + 'c3-selected-circle' + '-' + itemIndex)
appendOrUpdateCircle.bind(this)({
circle,
data,
itemIndex,
cx: this.circleX.bind(this),
cy: this.circleY.bind(this),
cssId: 'overlayed-circle',
})
}
chart.internal.setCurrentCircle = function (data, itemIndex) {
const circle = this.main
.select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id))
.selectAll('#current-circle')
appendOrUpdateCircle.bind(this)({
circle,
data,
itemIndex,
cx: this.circleX.bind(this),
cy: this.circleY.bind(this),
cssId: 'current-circle',
})
}
chart.internal.showTooltip = function (selectedData, element) {
const $$ = this
const config = $$.config
const forArc = $$.hasArcType()
const dataToShow = selectedData.filter((d) => d && (d.value || d.value === 0))
const positionFunction = config.tooltip_position || chart.internal.prototype.tooltipPosition
if (dataToShow.length === 0 || !config.tooltip_show) {
return
}
$$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style('display', 'flex')
// Get tooltip dimensions
const tWidth = $$.tooltip.property('offsetWidth')
const tHeight = $$.tooltip.property('offsetHeight')
const position = positionFunction.call(this, dataToShow, tWidth, tHeight, element)
// Set tooltip
$$.tooltip.style('top', position.top + 'px').style('left', position.left + 'px')
}
setTimeout(function () { setTimeout(function () {
setTickPosition('y', 0, -5, 8) setTickPosition('y', 0, -5, 8)
@ -310,84 +51,42 @@ export default class GasPriceChart extends Component {
d3.select('.c3-xgrid-focus line').attr('y2', 98) d3.select('.c3-xgrid-focus line').attr('y2', 98)
d3.select('.c3-chart').on('mouseout', () => { d3.select('.c3-chart').on('mouseout', () => {
const overLayedCircle = d3.select('#overlayed-circle') hideDataUI(chart, '#overlayed-circle')
if (!overLayedCircle.empty()) {
overLayedCircle.remove()
}
d3.select('.c3-tooltip-container').style('display', 'none !important')
}) })
const chartRect = d3.select('.c3-areas-data1')
const { x: chartXStart, width: chartWidth } = chartRect.node().getBoundingClientRect()
d3.select('.c3-chart').on('click', () => { d3.select('.c3-chart').on('click', () => {
const overlayedCircle = d3.select('#overlayed-circle') const { x: newGasPrice } = d3.select('#overlayed-circle').datum()
const numberOfValues = chart.internal.data.xs.data1.length updateCustomGasPrice(newGasPrice)
const { x: circleX, y: circleY } = overlayedCircle.node().getBoundingClientRect()
const { x: xData } = overlayedCircle.datum()
chart.internal.selectPoint({
x: circleX - chartXStart,
value: circleY - 1.5,
id: 'data1',
index: numberOfValues,
name: 'data1',
}, numberOfValues)
updateCustomGasPrice(xData)
}) })
setSelectedCircle({ chart, gasPrices, currentPrice, chartXStart, chartWidth }) const { x: chartXStart, width: chartWidth } = getCoordinateData('.c3-areas-data1')
d3.select('.c3-chart').on('mousemove', function () { handleChartUpdate ({
const chartMouseXPos = d3.event.clientX - chartXStart chart,
const posPercentile = chartMouseXPos / chartWidth gasPrices,
newPrice: currentPrice,
const currentPosValue = (gasPrices[gasPrices.length - 1] - gasPrices[0]) * posPercentile + gasPrices[0] cssId: '#set-circle',
const closestLowerValueIndex = gasPrices.findIndex((e, i, a) => {
return e <= currentPosValue && a[i + 1] >= currentPosValue
}) })
const closestLowerValue = gasPrices[closestLowerValueIndex]
const estimatedClosestLowerTimeEstimate = estimatedTimes[closestLowerValueIndex]
const closestHigherValueIndex = gasPrices.findIndex((e, i, a) => { d3.select('.c3-chart').on('mousemove', function () {
return e > currentPosValue const { currentPosValue, newTimeEstimate } = getNewXandTimeEstimate({
xMousePos: d3.event.clientX,
chartXStart,
chartWidth,
gasPrices,
estimatedTimes,
}) })
const closestHigherValue = gasPrices[closestHigherValueIndex]
if (!closestHigherValue || !closestLowerValue) { if (currentPosValue === null && newTimeEstimate === null) {
const overLayedCircle = d3.select('#overlayed-circle') hideDataUI(chart, '#overlayed-circle')
if (!overLayedCircle.empty()) {
overLayedCircle.remove()
}
d3.select('.c3-tooltip-container').style('display', 'none !important')
chart.internal.hideXGridFocus()
return
} }
const estimatedClosestHigherTimeEstimate = estimatedTimes[closestHigherValueIndex]
const slope = (estimatedClosestHigherTimeEstimate - estimatedClosestLowerTimeEstimate) / (closestHigherValue - closestLowerValue) const indexOfNewCircle = estimatedTimes.length + 1
const newTimeEstimate = -1 * (slope * (closestHigherValue - currentPosValue) - estimatedClosestHigherTimeEstimate) const dataUIObj = generateDataUIObj(currentPosValue, indexOfNewCircle, newTimeEstimate)
const newEstimatedTimes = [...estimatedTimes, newTimeEstimate] chart.internal.overlayPoint(dataUIObj, indexOfNewCircle)
chart.internal.overlayPoint({ chart.internal.showTooltip([dataUIObj], d3.select('.c3-areas-data1')._groups[0])
x: currentPosValue, chart.internal.showXGridFocus([dataUIObj])
value: newTimeEstimate,
id: 'data1',
index: newEstimatedTimes.length,
name: 'data1',
}, newEstimatedTimes.length)
chart.internal.showTooltip([{
x: currentPosValue,
value: newTimeEstimate,
id: 'data1',
index: newEstimatedTimes.length,
name: 'data1',
}], chartRect._groups[0])
chart.internal.showXGridFocus([{
x: currentPosValue,
value: newTimeEstimate,
id: 'data1',
index: newEstimatedTimes.length,
name: 'data1',
}])
}) })
}, 0) }, 0)
@ -395,22 +94,20 @@ export default class GasPriceChart extends Component {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
if (prevProps.currentPrice !== this.props.currentPrice) { const { gasPrices, currentPrice: newPrice } = this.props
const chartRect = d3.select('.c3-areas-data1')
const { x: chartXStart, width: chartWidth } = chartRect.node().getBoundingClientRect() if (prevProps.currentPrice !== newPrice) {
setSelectedCircle({ handleChartUpdate ({
chart: this.chart, chart: this.chart,
currentPrice: this.props.currentPrice, gasPrices,
gasPrices: this.props.priceAndTimeEstimates.map(({ gasprice }) => gasprice), newPrice,
chartXStart, cssId: '#set-circle',
chartWidth,
}) })
} }
} }
componentDidMount () { componentDidMount () {
const { currentPrice, priceAndTimeEstimates, updateCustomGasPrice } = this.props this.renderChart(this.props)
this.renderChart(currentPrice, priceAndTimeEstimates, updateCustomGasPrice)
} }
render () { render () {

@ -0,0 +1,307 @@
import * as d3 from 'd3'
import c3 from 'c3'
export function getCoordinateData (selector) {
return d3.select(selector).node().getBoundingClientRect()
}
export function generateDataUIObj (x, index, value) {
return {
x,
value,
index,
id: 'data1',
name: 'data1',
}
}
export function handleChartUpdate ({ chart, gasPrices, newPrice, cssId }) {
const {
closestLowerValueIndex,
closestLowerValue,
closestHigherValueIndex,
closestHigherValue,
} = getAdjacentGasPrices({ gasPrices, priceToPosition: newPrice })
if (closestLowerValue && closestHigherValue) {
setSelectedCircle({
chart,
newPrice,
closestLowerValueIndex,
closestLowerValue,
closestHigherValueIndex,
closestHigherValue,
})
} else {
hideDataUI(chart, cssId)
}
}
export function getAdjacentGasPrices({ gasPrices, priceToPosition }) {
const closestLowerValueIndex = gasPrices.findIndex((e, i, a) => e <= priceToPosition && a[i + 1] >= priceToPosition)
const closestHigherValueIndex = gasPrices.findIndex((e, i, a) => e > priceToPosition)
return {
closestLowerValueIndex,
closestHigherValueIndex,
closestHigherValue: gasPrices[closestHigherValueIndex],
closestLowerValue: gasPrices[closestLowerValueIndex],
}
}
export function extrapolateY ({ higherY, lowerY, higherX, lowerX, xForExtrapolation }) {
const slope = (higherY - lowerY) / (higherX - lowerX)
const newTimeEstimate = -1 * (slope * (higherX - xForExtrapolation) - higherY)
return newTimeEstimate
}
export function getNewXandTimeEstimate ({ xMousePos, chartXStart, chartWidth, gasPrices, estimatedTimes }) {
const chartMouseXPos = xMousePos - chartXStart
const posPercentile = chartMouseXPos / chartWidth
const currentPosValue = (gasPrices[gasPrices.length - 1] - gasPrices[0]) * posPercentile + gasPrices[0]
const {
closestLowerValueIndex,
closestLowerValue,
closestHigherValueIndex,
closestHigherValue,
} = getAdjacentGasPrices({ gasPrices, priceToPosition: currentPosValue })
return !closestHigherValue || !closestLowerValue
? {
currentPosValue: null,
newTimeEstimate: null,
}
: {
currentPosValue,
newTimeEstimate: extrapolateY ({
higherY: estimatedTimes[closestHigherValueIndex],
lowerY: estimatedTimes[closestLowerValueIndex],
higherX: closestHigherValue,
lowerX: closestLowerValue,
xForExtrapolation: currentPosValue,
}),
}
}
export function hideDataUI (chart, dataNodeId) {
const overLayedCircle = d3.select(dataNodeId)
if (!overLayedCircle.empty()) {
overLayedCircle.remove()
}
d3.select('.c3-tooltip-container').style('display', 'none !important')
chart.internal.hideXGridFocus()
}
export function setTickPosition (axis, n, newPosition, secondNewPosition) {
const positionToShift = axis === 'y' ? 'x' : 'y'
const secondPositionToShift = axis === 'y' ? 'y' : 'x'
d3.select('#chart')
.select(`.c3-axis-${axis}`)
.selectAll('.tick')
.filter((d, i) => i === n)
.select('text')
.attr(positionToShift, 0)
.select('tspan')
.attr(positionToShift, newPosition)
.attr(secondPositionToShift, secondNewPosition || 0)
.style('visibility', 'visible')
}
export function appendOrUpdateCircle ({ data, itemIndex, cx, cy, cssId, appendOnly }) {
const circle = this.main
.select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id))
.selectAll('.' + 'c3-selected-circle' + '-' + itemIndex)
if (appendOnly || circle.empty()) {
circle.data([data])
.enter().append('circle')
.attr('class', () => this.generateClass('c3-selected-circle', itemIndex))
.attr('id', cssId)
.attr('cx', cx)
.attr('cy', cy)
.attr('stroke', () => this.color(data))
.attr('r', 6)
} else {
circle.data([data])
.attr('cx', cx)
.attr('cy', cy)
}
}
export function setSelectedCircle ({
chart,
newPrice,
closestLowerValueIndex,
closestLowerValue,
closestHigherValueIndex,
closestHigherValue,
}) {
const numberOfValues = chart.internal.data.xs.data1.length
const { x: lowerX, y: lowerY } = getCoordinateData(`.c3-circle-${closestLowerValueIndex}`)
const { x: higherX, y: higherY } = getCoordinateData(`.c3-circle-${closestHigherValueIndex}`)
const currentX = lowerX + (higherX - lowerX) * (newPrice - closestLowerValue) / (closestHigherValue - closestLowerValue)
const newTimeEstimate = extrapolateY ({ higherY, lowerY, higherX, lowerX, xForExtrapolation: currentX })
chart.internal.selectPoint(
generateDataUIObj(currentX, numberOfValues, newTimeEstimate),
numberOfValues
)
}
export function generateChart (gasPrices, estimatedTimes, gasPricesMax, estimatedTimesMax) {
const chart = c3.generate({
size: {
height: 165,
},
transition: {
duration: 0,
},
padding: {left: 20, right: 15, top: 6, bottom: 10},
data: {
x: 'x',
columns: [
['x', ...gasPrices],
['data1', ...estimatedTimes],
],
types: {
data1: 'area',
},
selection: {
enabled: false,
},
},
color: {
data1: '#259de5',
},
axis: {
x: {
min: gasPrices[0],
max: gasPricesMax,
tick: {
values: [Math.floor(gasPrices[0]), Math.ceil(gasPricesMax)],
outer: false,
format: function (val) { return val + ' GWEI' },
},
padding: {left: gasPricesMax / 50, right: gasPricesMax / 50},
label: {
text: 'Gas Price ($)',
position: 'outer-center',
},
},
y: {
padding: {top: 7, bottom: 7},
tick: {
values: [Math.floor(estimatedTimesMax * 0.05), Math.ceil(estimatedTimesMax * 0.97)],
outer: false,
},
label: {
text: 'Confirmation time (sec)',
position: 'outer-middle',
},
min: 0,
},
},
legend: {
show: false,
},
grid: {
x: {},
lines: {
front: false,
},
},
point: {
focus: {
expand: {
enabled: false,
r: 3.5,
},
},
},
tooltip: {
format: {
title: (v) => v.toPrecision(4),
},
contents: function (d) {
const titleFormat = this.config.tooltip_format_title
let text
d.forEach(el => {
if (el && (el.value || el.value === 0) && !text) {
text = "<table class='" + 'custom-tooltip' + "'>" + "<tr><th colspan='2'>" + titleFormat(el.x) + '</th></tr>'
}
})
return text + '</table>' + "<div class='tooltip-arrow'></div>"
},
position: function (data) {
if (d3.select('#overlayed-circle').empty()) {
return { top: -100, left: -100 }
}
const { x: circleX, y: circleY, width: circleWidth } = getCoordinateData('#overlayed-circle')
const { x: chartXStart, y: chartYStart } = getCoordinateData('.c3-chart')
// TODO: Confirm the below constants work with all data sets and screen sizes
const flipTooltip = circleY - circleWidth < chartYStart + 5
d3
.select('.tooltip-arrow')
.style('margin-top', flipTooltip ? '-16px' : '4px')
return {
top: circleY - chartYStart - 19 + (flipTooltip ? circleWidth + 38 : 0),
left: circleX - chartXStart + circleWidth - (gasPricesMax / 50),
}
},
show: true,
},
})
chart.internal.selectPoint = function (data, itemIndex = (data.index || 0)) {
const { x: chartXStart, y: chartYStart } = getCoordinateData('.c3-areas-data1')
d3.select('#set-circle').remove()
appendOrUpdateCircle.bind(this)({
data,
itemIndex,
cx: () => data.x - chartXStart + 11,
cy: () => data.value - chartYStart + 10,
cssId: 'set-circle',
appendOnly: true,
})
}
chart.internal.overlayPoint = function (data, itemIndex) {
appendOrUpdateCircle.bind(this)({
data,
itemIndex,
cx: this.circleX.bind(this),
cy: this.circleY.bind(this),
cssId: 'overlayed-circle',
})
}
chart.internal.showTooltip = function (selectedData, element) {
const dataToShow = selectedData.filter((d) => d && (d.value || d.value === 0))
if (dataToShow.length) {
this.tooltip.html(
this.config.tooltip_contents.call(this, selectedData, this.axis.getXAxisTickFormat(), this.getYFormat(), this.color)
).style('display', 'flex')
// Get tooltip dimensions
const tWidth = this.tooltip.property('offsetWidth')
const tHeight = this.tooltip.property('offsetHeight')
const position = this.config.tooltip_position.call(this, dataToShow, tWidth, tHeight, element)
// Set tooltip
this.tooltip.style('top', position.top + 'px').style('left', position.left + 'px')
}
}
return chart
}
Loading…
Cancel
Save