Fix #278 (display 'flex' instead of 'block')
Fix syncing of chart legends (#284).
This commit is contained in:
@ -182,6 +182,9 @@ if (!window.Chart) {
|
|||||||
|
|
||||||
class Legend {
|
class Legend {
|
||||||
constructor(chart) {
|
constructor(chart) {
|
||||||
|
this.legendHandler = this.legendHandler.bind(this)
|
||||||
|
|
||||||
|
this.chart = chart
|
||||||
this.ohlcEnabled = false
|
this.ohlcEnabled = false
|
||||||
this.percentEnabled = false
|
this.percentEnabled = false
|
||||||
this.linesEnabled = false
|
this.linesEnabled = false
|
||||||
@ -207,80 +210,12 @@ if (!window.Chart) {
|
|||||||
|
|
||||||
this.makeLines(chart)
|
this.makeLines(chart)
|
||||||
|
|
||||||
let legendItemFormat = (num, decimal) => num.toFixed(decimal).toString().padStart(8, ' ')
|
chart.chart.subscribeCrosshairMove(this.legendHandler)
|
||||||
|
|
||||||
let shorthandFormat = (num) => {
|
|
||||||
const absNum = Math.abs(num)
|
|
||||||
if (absNum >= 1000000) {
|
|
||||||
return (num / 1000000).toFixed(1) + 'M';
|
|
||||||
} else if (absNum >= 1000) {
|
|
||||||
return (num / 1000).toFixed(1) + 'K';
|
|
||||||
}
|
|
||||||
return num.toString().padStart(8, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
chart.chart.subscribeCrosshairMove((param) => {
|
|
||||||
let options = chart.series.options()
|
|
||||||
if (!param.time) {
|
|
||||||
this.candle.style.color = 'transparent'
|
|
||||||
this.candle.innerHTML = this.candle.innerHTML.replace(options['upColor'], '').replace(options['downColor'], '')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let data = param.seriesData.get(chart.series);
|
|
||||||
this.candle.style.color = ''
|
|
||||||
let str = '<span style="line-height: 1.8;">'
|
|
||||||
if (data) {
|
|
||||||
if (this.ohlcEnabled) {
|
|
||||||
str += `O ${legendItemFormat(data.open, chart.precision)} `
|
|
||||||
str += `| H ${legendItemFormat(data.high, chart.precision)} `
|
|
||||||
str += `| L ${legendItemFormat(data.low, chart.precision)} `
|
|
||||||
str += `| C ${legendItemFormat(data.close, chart.precision)} `
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.percentEnabled) {
|
|
||||||
let percentMove = ((data.close - data.open) / data.open) * 100
|
|
||||||
let color = percentMove > 0 ? options['upColor'] : options['downColor']
|
|
||||||
let percentStr = `${percentMove >= 0 ? '+' : ''}${percentMove.toFixed(2)} %`
|
|
||||||
|
|
||||||
if (this.colorBasedOnCandle) {
|
|
||||||
str += `| <span style="color: ${color};">${percentStr}</span>`
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
str += '| ' + percentStr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let volumeData = param.seriesData.get(chart.volumeSeries)
|
|
||||||
if (volumeData) {
|
|
||||||
str += this.ohlcEnabled ? `<br>V ${shorthandFormat(volumeData.value)}` : ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.candle.innerHTML = str + '</span>'
|
|
||||||
|
|
||||||
this.lines.forEach((line) => {
|
|
||||||
if (!this.linesEnabled) {
|
|
||||||
line.row.style.display = 'none'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
line.row.style.display = 'flex'
|
|
||||||
if (!param.seriesData.get(line.line.series)) return
|
|
||||||
let price = param.seriesData.get(line.line.series).value
|
|
||||||
|
|
||||||
if (line.line.type === 'histogram') {
|
|
||||||
price = shorthandFormat(price)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
price = legendItemFormat(price, line.line.precision)
|
|
||||||
}
|
|
||||||
line.div.innerHTML = `<span style="color: ${line.solid};">▨</span> ${line.line.name} : ${price}`
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
// Exclude the chart attribute from serialization
|
// Exclude the chart attribute from serialization
|
||||||
const {lines, ...serialized} = this;
|
const {lines, chart, ...serialized} = this;
|
||||||
return serialized;
|
return serialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,6 +287,94 @@ if (!window.Chart) {
|
|||||||
solid: line.color.startsWith('rgba') ? line.color.replace(/[^,]+(?=\))/, '1') : line.color
|
solid: line.color.startsWith('rgba') ? line.color.replace(/[^,]+(?=\))/, '1') : line.color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
legendItemFormat(num, decimal) { return num.toFixed(decimal).toString().padStart(8, ' ') }
|
||||||
|
|
||||||
|
shorthandFormat(num) {
|
||||||
|
const absNum = Math.abs(num)
|
||||||
|
if (absNum >= 1000000) {
|
||||||
|
return (num / 1000000).toFixed(1) + 'M';
|
||||||
|
} else if (absNum >= 1000) {
|
||||||
|
return (num / 1000).toFixed(1) + 'K';
|
||||||
|
}
|
||||||
|
return num.toString().padStart(8, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
legendHandler(param) {
|
||||||
|
let options = this.chart.series.options()
|
||||||
|
|
||||||
|
if (!param.time) {
|
||||||
|
this.candle.style.color = 'transparent'
|
||||||
|
this.candle.innerHTML = this.candle.innerHTML.replace(options['upColor'], '').replace(options['downColor'], '')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let usingPoint = !param.point && param.time
|
||||||
|
|
||||||
|
let data, logical
|
||||||
|
|
||||||
|
if (usingPoint) {
|
||||||
|
let coordinate = this.chart.chart.timeScale().timeToCoordinate(param.time)
|
||||||
|
logical = this.chart.chart.timeScale().coordinateToLogical(coordinate)
|
||||||
|
data = this.chart.series.dataByIndex(logical)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data = param.seriesData.get(this.chart.series);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.candle.style.color = ''
|
||||||
|
let str = '<span style="line-height: 1.8;">'
|
||||||
|
if (data) {
|
||||||
|
if (this.ohlcEnabled) {
|
||||||
|
str += `O ${this.legendItemFormat(data.open, this.chart.precision)} `
|
||||||
|
str += `| H ${this.legendItemFormat(data.high, this.chart.precision)} `
|
||||||
|
str += `| L ${this.legendItemFormat(data.low, this.chart.precision)} `
|
||||||
|
str += `| C ${this.legendItemFormat(data.close, this.chart.precision)} `
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.percentEnabled) {
|
||||||
|
let percentMove = ((data.close - data.open) / data.open) * 100
|
||||||
|
let color = percentMove > 0 ? options['upColor'] : options['downColor']
|
||||||
|
let percentStr = `${percentMove >= 0 ? '+' : ''}${percentMove.toFixed(2)} %`
|
||||||
|
|
||||||
|
if (this.colorBasedOnCandle) {
|
||||||
|
str += `| <span style="color: ${color};">${percentStr}</span>`
|
||||||
|
} else {
|
||||||
|
str += '| ' + percentStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let volumeData = param.seriesData.get(this.chart.volumeSeries)
|
||||||
|
if (volumeData) {
|
||||||
|
str += this.ohlcEnabled ? `<br>V ${this.shorthandFormat(volumeData.value)}` : ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.candle.innerHTML = str + '</span>'
|
||||||
|
|
||||||
|
this.lines.forEach((line) => {
|
||||||
|
if (!this.linesEnabled) {
|
||||||
|
line.row.style.display = 'none'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
line.row.style.display = 'flex'
|
||||||
|
|
||||||
|
let price
|
||||||
|
if (usingPoint) {
|
||||||
|
price = line.line.series.dataByIndex(logical)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
price = param.seriesData.get(line.line.series)
|
||||||
|
}
|
||||||
|
if (!price) return
|
||||||
|
else price = price.value
|
||||||
|
|
||||||
|
if (line.line.type === 'histogram') {
|
||||||
|
price = this.shorthandFormat(price)
|
||||||
|
} else {
|
||||||
|
price = this.legendItemFormat(price, line.line.precision)
|
||||||
|
}
|
||||||
|
line.div.innerHTML = `<span style="color: ${line.solid};">▨</span> ${line.line.name} : ${price}`
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.Legend = Legend
|
window.Legend = Legend
|
||||||
@ -359,12 +382,13 @@ if (!window.Chart) {
|
|||||||
|
|
||||||
function syncCharts(childChart, parentChart) {
|
function syncCharts(childChart, parentChart) {
|
||||||
|
|
||||||
function crosshairHandler(chart, series, point) {
|
function crosshairHandler(chart, point) {
|
||||||
if (!point) {
|
if (!point) {
|
||||||
chart.clearCrosshairPosition()
|
chart.chart.clearCrosshairPosition()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
chart.setCrosshairPosition(point.value || point.close, point.time, series);
|
chart.chart.setCrosshairPosition(point.value || point.close, point.time, chart.series);
|
||||||
|
chart.legend.legendHandler(point)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPoint(series, param) {
|
function getPoint(series, param) {
|
||||||
@ -376,10 +400,10 @@ function syncCharts(childChart, parentChart) {
|
|||||||
let setParentRange = (timeRange) => parentChart.chart.timeScale().setVisibleLogicalRange(timeRange)
|
let setParentRange = (timeRange) => parentChart.chart.timeScale().setVisibleLogicalRange(timeRange)
|
||||||
|
|
||||||
let setParentCrosshair = (param) => {
|
let setParentCrosshair = (param) => {
|
||||||
crosshairHandler(parentChart.chart, parentChart.series, getPoint(childChart.series, param))
|
crosshairHandler(parentChart, getPoint(childChart.series, param))
|
||||||
}
|
}
|
||||||
let setChildCrosshair = (param) => {
|
let setChildCrosshair = (param) => {
|
||||||
crosshairHandler(childChart.chart, childChart.series, getPoint(parentChart.series, param))
|
crosshairHandler(childChart, getPoint(parentChart.series, param))
|
||||||
}
|
}
|
||||||
|
|
||||||
let selected = parentChart
|
let selected = parentChart
|
||||||
|
|||||||
@ -109,6 +109,6 @@ class Table(Pane, dict):
|
|||||||
def visible(self, visible: bool):
|
def visible(self, visible: bool):
|
||||||
self.is_shown = visible
|
self.is_shown = visible
|
||||||
self.run_script(f"""
|
self.run_script(f"""
|
||||||
{self.id}.container.style.display = '{'block' if visible else 'none'}'
|
{self.id}.container.style.display = '{'flex' if visible else 'none'}'
|
||||||
{self.id}.container.{'add' if visible else 'remove'}EventListener('mousedown', {self.id}.onMouseDown)
|
{self.id}.container.{'add' if visible else 'remove'}EventListener('mousedown', {self.id}.onMouseDown)
|
||||||
""")
|
""")
|
||||||
|
|||||||
Reference in New Issue
Block a user