implement toggleable buttons; menu items can now be changed; add horizontal line and vertical line labels
This commit is contained in:
@ -8,7 +8,7 @@ import pandas as pd
|
|||||||
|
|
||||||
from .table import Table
|
from .table import Table
|
||||||
from .toolbox import ToolBox
|
from .toolbox import ToolBox
|
||||||
from .drawings import Box, HorizontalLine, TrendLine, TwoPointDrawing, VerticalSpan
|
from .drawings import Box, HorizontalLine, TrendLine, TwoPointDrawing, VerticalLine, VerticalSpan
|
||||||
from .topbar import TopBar
|
from .topbar import TopBar
|
||||||
from .util import (
|
from .util import (
|
||||||
BulkRunScript, Pane, Events, IDGen, as_enum, jbool, js_json, TIME, NUM, FLOAT,
|
BulkRunScript, Pane, Events, IDGen, as_enum, jbool, js_json, TIME, NUM, FLOAT,
|
||||||
@ -728,10 +728,20 @@ class AbstractChart(Candlestick, Pane):
|
|||||||
width: int = 2,
|
width: int = 2,
|
||||||
style: LINE_STYLE = 'solid'
|
style: LINE_STYLE = 'solid'
|
||||||
) -> Line:
|
) -> Line:
|
||||||
line = Line(self, '', color, style, width, False, False, False)
|
# TODO
|
||||||
line._set_trend(start_time, value, start_time, value, ray=True, round=round)
|
line = RayLine(self, '', color, style, width, False, False, False)
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
def vertical_line(
|
||||||
|
self,
|
||||||
|
time: TIME,
|
||||||
|
color: str = '#1E80F0',
|
||||||
|
width: int = 2,
|
||||||
|
style: LINE_STYLE ='solid',
|
||||||
|
text: str = ''
|
||||||
|
) -> VerticalLine:
|
||||||
|
return VerticalLine(*locals().values())
|
||||||
|
|
||||||
def set_visible_range(self, start_time: TIME, end_time: TIME):
|
def set_visible_range(self, start_time: TIME, end_time: TIME):
|
||||||
self.run_script(f'''
|
self.run_script(f'''
|
||||||
{self.id}.chart.timeScale().setVisibleRange({{
|
{self.id}.chart.timeScale().setVisibleRange({{
|
||||||
|
|||||||
@ -24,6 +24,13 @@ class Drawing(Pane):
|
|||||||
"""
|
"""
|
||||||
self.run_script(f'{self.id}.detach()')
|
self.run_script(f'{self.id}.detach()')
|
||||||
|
|
||||||
|
def options(self, color='#1E80F0', style='solid', width=4):
|
||||||
|
self.run_script(f'''{self.id}.applyOptions({{
|
||||||
|
lineColor: '{color}',
|
||||||
|
lineStyle: {as_enum(style, LINE_STYLE)},
|
||||||
|
width: {width},
|
||||||
|
}})''')
|
||||||
|
|
||||||
class TwoPointDrawing(Drawing):
|
class TwoPointDrawing(Drawing):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -76,6 +83,8 @@ class HorizontalLine(Drawing):
|
|||||||
{{
|
{{
|
||||||
lineColor: '{color}',
|
lineColor: '{color}',
|
||||||
lineStyle: {as_enum(style, LINE_STYLE)},
|
lineStyle: {as_enum(style, LINE_STYLE)},
|
||||||
|
width: {width},
|
||||||
|
text: `{text}`,
|
||||||
}},
|
}},
|
||||||
callbackName={f"'{self.id}'" if func else 'null'}
|
callbackName={f"'{self.id}'" if func else 'null'}
|
||||||
)
|
)
|
||||||
@ -103,22 +112,25 @@ class HorizontalLine(Drawing):
|
|||||||
# self.run_script(f'{self.id}.updatePrice({price})')
|
# self.run_script(f'{self.id}.updatePrice({price})')
|
||||||
self.price = price
|
self.price = price
|
||||||
|
|
||||||
def label(self, text: str): # TODO
|
def options(self, color='#1E80F0', style='solid', width=4, text=''):
|
||||||
self.run_script(f'{self.id}.updateLabel("{text}")')
|
super().options(color, style, width)
|
||||||
|
self.run_script(f'{self.id}.applyOptions({{text: `{text}`}})')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class VerticalLine(Drawing):
|
class VerticalLine(Drawing):
|
||||||
def __init__(self, chart, time, color, width, style, text, axis_label_visible, func):
|
def __init__(self, chart, time, color, width, style, text, func=None):
|
||||||
super().__init__(chart, func)
|
super().__init__(chart, func)
|
||||||
self.time = time
|
self.time = time
|
||||||
self.run_script(f'''
|
self.run_script(f'''
|
||||||
|
|
||||||
{self.id} = new Lib.VerticalLine(
|
{self.id} = new Lib.VerticalLine(
|
||||||
{{time: {time}}},
|
{{time: {self.chart._single_datetime_format(time)}}},
|
||||||
{{
|
{{
|
||||||
lineColor: '{color}',
|
lineColor: '{color}',
|
||||||
lineStyle: {as_enum(style, LINE_STYLE)},
|
lineStyle: {as_enum(style, LINE_STYLE)},
|
||||||
|
width: {width},
|
||||||
|
text: `{text}`,
|
||||||
}},
|
}},
|
||||||
callbackName={f"'{self.id}'" if func else 'null'}
|
callbackName={f"'{self.id}'" if func else 'null'}
|
||||||
)
|
)
|
||||||
@ -130,8 +142,9 @@ class VerticalLine(Drawing):
|
|||||||
# self.run_script(f'{self.id}.updatePrice({price})')
|
# self.run_script(f'{self.id}.updatePrice({price})')
|
||||||
self.price = price
|
self.price = price
|
||||||
|
|
||||||
def label(self, text: str): # TODO
|
def options(self, color='#1E80F0', style='solid', width=4, text=''):
|
||||||
self.run_script(f'{self.id}.updateLabel("{text}")')
|
super().options(color, style, width)
|
||||||
|
self.run_script(f'{self.id}.applyOptions({{text: `{text}`}})')
|
||||||
|
|
||||||
|
|
||||||
class Box(TwoPointDrawing):
|
class Box(TwoPointDrawing):
|
||||||
@ -188,13 +201,14 @@ class TrendLine(TwoPointDrawing):
|
|||||||
end_value,
|
end_value,
|
||||||
round,
|
round,
|
||||||
{
|
{
|
||||||
"lineColor": f'"line_color"',
|
"lineColor": f'"{line_color}"',
|
||||||
"width": width,
|
"width": width,
|
||||||
"lineStyle": as_enum(style, LINE_STYLE)
|
"lineStyle": as_enum(style, LINE_STYLE)
|
||||||
},
|
},
|
||||||
func
|
func
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO reimplement/fix
|
||||||
class VerticalSpan(Pane):
|
class VerticalSpan(Pane):
|
||||||
def __init__(self, series: 'SeriesCommon', start_time: Union[TIME, tuple, list], end_time: Optional[TIME] = None,
|
def __init__(self, series: 'SeriesCommon', start_time: Union[TIME, tuple, list], end_time: Optional[TIME] = None,
|
||||||
color: str = 'rgba(252, 219, 3, 0.2)'):
|
color: str = 'rgba(252, 219, 3, 0.2)'):
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -8,11 +8,14 @@ ALIGN = Literal['left', 'right']
|
|||||||
|
|
||||||
|
|
||||||
class Widget(Pane):
|
class Widget(Pane):
|
||||||
def __init__(self, topbar, value, func: callable = None):
|
def __init__(self, topbar, value, func: callable = None, convert_boolean=False):
|
||||||
super().__init__(topbar.win)
|
super().__init__(topbar.win)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def wrapper(v):
|
def wrapper(v):
|
||||||
|
if convert_boolean:
|
||||||
|
self.value = False if v == 'false' else True
|
||||||
|
else:
|
||||||
self.value = v
|
self.value = v
|
||||||
func(topbar._chart)
|
func(topbar._chart)
|
||||||
|
|
||||||
@ -54,6 +57,7 @@ class MenuWidget(Widget):
|
|||||||
{self.id} = {topbar.id}.makeMenu({list(options)}, "{default}", {jbool(separator)}, "{self.id}", "{align}")
|
{self.id} = {topbar.id}.makeMenu({list(options)}, "{default}", {jbool(separator)}, "{self.id}", "{align}")
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
# TODO this will probably need to be fixed
|
||||||
def set(self, option):
|
def set(self, option):
|
||||||
if option not in self.options:
|
if option not in self.options:
|
||||||
raise ValueError(f"Option {option} not in menu options ({self.options})")
|
raise ValueError(f"Option {option} not in menu options ({self.options})")
|
||||||
@ -63,15 +67,19 @@ class MenuWidget(Widget):
|
|||||||
''')
|
''')
|
||||||
self.win.handlers[self.id](option)
|
self.win.handlers[self.id](option)
|
||||||
|
|
||||||
|
def update_items(self, *items: str):
|
||||||
|
self.options = list(items)
|
||||||
|
self.run_script(f'{self.id}.updateMenuItems({self.options})')
|
||||||
|
|
||||||
|
|
||||||
class ButtonWidget(Widget):
|
class ButtonWidget(Widget):
|
||||||
def __init__(self, topbar, button, separator, align, func):
|
def __init__(self, topbar, button, separator, align, toggle, func):
|
||||||
super().__init__(topbar, value=button, func=func)
|
super().__init__(topbar, value=False, func=func, convert_boolean=toggle)
|
||||||
self.run_script(
|
self.run_script(
|
||||||
f'{self.id} = {topbar.id}.makeButton("{button}", "{self.id}", {jbool(separator)}, true, "{align}")')
|
f'{self.id} = {topbar.id}.makeButton("{button}", "{self.id}", {jbool(separator)}, true, "{align}", {jbool(toggle)})')
|
||||||
|
|
||||||
def set(self, string):
|
def set(self, string):
|
||||||
self.value = string
|
# self.value = string
|
||||||
self.run_script(f'{self.id}.elem.innerText = "{string}"')
|
self.run_script(f'{self.id}.elem.innerText = "{string}"')
|
||||||
|
|
||||||
|
|
||||||
@ -112,6 +120,6 @@ class TopBar(Pane):
|
|||||||
self._widgets[name] = TextWidget(self, initial_text, align)
|
self._widgets[name] = TextWidget(self, initial_text, align)
|
||||||
|
|
||||||
def button(self, name, button_text: str, separator: bool = True,
|
def button(self, name, button_text: str, separator: bool = True,
|
||||||
align: ALIGN = 'left', func: callable = None):
|
align: ALIGN = 'left', toggle: bool = False, func: callable = None):
|
||||||
self._create()
|
self._create()
|
||||||
self._widgets[name] = ButtonWidget(self, button_text, separator, align, func)
|
self._widgets[name] = ButtonWidget(self, button_text, separator, align, toggle, func)
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export class Legend {
|
|||||||
this.legendHandler = this.legendHandler.bind(this)
|
this.legendHandler = this.legendHandler.bind(this)
|
||||||
|
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.ohlcEnabled = true;
|
this.ohlcEnabled = false;
|
||||||
this.percentEnabled = false
|
this.percentEnabled = false
|
||||||
this.linesEnabled = false
|
this.linesEnabled = false
|
||||||
this.colorBasedOnCandle = false
|
this.colorBasedOnCandle = false
|
||||||
|
|||||||
59
src/general/menu.ts
Normal file
59
src/general/menu.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { GlobalParams } from "./global-params";
|
||||||
|
|
||||||
|
declare const window: GlobalParams
|
||||||
|
|
||||||
|
export class Menu {
|
||||||
|
private div: HTMLDivElement;
|
||||||
|
private isOpen: boolean = false;
|
||||||
|
private widget: any;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private makeButton: Function,
|
||||||
|
private callbackName: string,
|
||||||
|
items: string[],
|
||||||
|
activeItem: string,
|
||||||
|
separator: boolean,
|
||||||
|
align: 'right'|'left') {
|
||||||
|
|
||||||
|
this.div = document.createElement('div')
|
||||||
|
this.div.classList.add('topbar-menu');
|
||||||
|
|
||||||
|
this.widget = this.makeButton(activeItem+' ↓', null, separator, true, align)
|
||||||
|
|
||||||
|
this.updateMenuItems(items)
|
||||||
|
|
||||||
|
this.widget.elem.addEventListener('click', () => {
|
||||||
|
this.isOpen = !this.isOpen;
|
||||||
|
if (!this.isOpen) {
|
||||||
|
this.div.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let rect = this.widget.elem.getBoundingClientRect()
|
||||||
|
this.div.style.display = 'flex'
|
||||||
|
this.div.style.flexDirection = 'column'
|
||||||
|
|
||||||
|
let center = rect.x+(rect.width/2)
|
||||||
|
this.div.style.left = center-(this.div.clientWidth/2)+'px'
|
||||||
|
this.div.style.top = rect.y+rect.height+'px'
|
||||||
|
})
|
||||||
|
document.body.appendChild(this.div)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMenuItems(items: string[]) {
|
||||||
|
this.div.innerHTML = '';
|
||||||
|
|
||||||
|
items.forEach(text => {
|
||||||
|
let button = this.makeButton(text, null, false, false)
|
||||||
|
button.elem.addEventListener('click', () => {
|
||||||
|
this.widget.elem.innerText = button.elem.innerText+' ↓'
|
||||||
|
window.callbackFunction(`${this.callbackName}_~_${button.elem.innerText}`)
|
||||||
|
this.div.style.display = 'none'
|
||||||
|
this.isOpen = false
|
||||||
|
});
|
||||||
|
button.elem.style.margin = '4px 4px'
|
||||||
|
button.elem.style.padding = '2px 2px'
|
||||||
|
this.div.appendChild(button.elem)
|
||||||
|
})
|
||||||
|
this.widget.elem.innerText = items[0]+' ↓';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { GlobalParams } from "./global-params";
|
import { GlobalParams } from "./global-params";
|
||||||
import { Handler } from "./handler";
|
import { Handler } from "./handler";
|
||||||
|
import { Menu } from "./menu";
|
||||||
|
|
||||||
declare const window: GlobalParams
|
declare const window: GlobalParams
|
||||||
|
|
||||||
@ -85,44 +86,11 @@ export class TopBar {
|
|||||||
return textBox
|
return textBox
|
||||||
}
|
}
|
||||||
|
|
||||||
makeMenu(items: string[], activeItem: string, separator: boolean, callbackName: string, align='right') {
|
makeMenu(items: string[], activeItem: string, separator: boolean, callbackName: string, align: 'right'|'left') {
|
||||||
let menu = document.createElement('div')
|
return new Menu(this.makeButton.bind(this), callbackName, items, activeItem, separator, align)
|
||||||
menu.classList.add('topbar-menu');
|
|
||||||
|
|
||||||
let menuOpen = false;
|
|
||||||
items.forEach(text => {
|
|
||||||
let button = this.makeButton(text, null, false, false)
|
|
||||||
button.elem.addEventListener('click', () => {
|
|
||||||
widget.elem.innerText = button.elem.innerText+' ↓'
|
|
||||||
window.callbackFunction(`${callbackName}_~_${button.elem.innerText}`)
|
|
||||||
menu.style.display = 'none'
|
|
||||||
menuOpen = false
|
|
||||||
});
|
|
||||||
button.elem.style.margin = '4px 4px'
|
|
||||||
button.elem.style.padding = '2px 2px'
|
|
||||||
menu.appendChild(button.elem)
|
|
||||||
})
|
|
||||||
let widget =
|
|
||||||
this.makeButton(activeItem+' ↓', null, separator, true, align)
|
|
||||||
|
|
||||||
widget.elem.addEventListener('click', () => {
|
|
||||||
menuOpen = !menuOpen
|
|
||||||
if (!menuOpen) {
|
|
||||||
menu.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let rect = widget.elem.getBoundingClientRect()
|
|
||||||
menu.style.display = 'flex'
|
|
||||||
menu.style.flexDirection = 'column'
|
|
||||||
|
|
||||||
let center = rect.x+(rect.width/2)
|
|
||||||
menu.style.left = center-(menu.clientWidth/2)+'px'
|
|
||||||
menu.style.top = rect.y+rect.height+'px'
|
|
||||||
})
|
|
||||||
document.body.appendChild(menu)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeButton(defaultText: string, callbackName: string | null, separator: boolean, append=true, align='left') {
|
makeButton(defaultText: string, callbackName: string | null, separator: boolean, append=true, align='left', toggle=false) {
|
||||||
let button = document.createElement('button')
|
let button = document.createElement('button')
|
||||||
button.classList.add('topbar-button');
|
button.classList.add('topbar-button');
|
||||||
// button.style.color = window.pane.color
|
// button.style.color = window.pane.color
|
||||||
@ -137,7 +105,19 @@ export class TopBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (callbackName) {
|
if (callbackName) {
|
||||||
button.addEventListener('click', () => window.callbackFunction(`${widget.callbackName}_~_${button.innerText}`));
|
let handler;
|
||||||
|
if (toggle) {
|
||||||
|
let state = false;
|
||||||
|
handler = () => {
|
||||||
|
state = !state
|
||||||
|
window.callbackFunction(`${widget.callbackName}_~_${state}`)
|
||||||
|
button.style.backgroundColor = state ? 'var(--active-bg-color)' : '';
|
||||||
|
button.style.color = state ? 'var(--active-color)' : '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handler = () => window.callbackFunction(`${widget.callbackName}_~_${button.innerText}`)
|
||||||
|
}
|
||||||
|
button.addEventListener('click', handler);
|
||||||
}
|
}
|
||||||
if (append) this.appendWidget(button, align, separator)
|
if (append) this.appendWidget(button, align, separator)
|
||||||
return widget
|
return widget
|
||||||
|
|||||||
37
src/horizontal-line/axis-view.ts
Normal file
37
src/horizontal-line/axis-view.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { Coordinate, ISeriesPrimitiveAxisView, PriceFormatBuiltIn } from 'lightweight-charts';
|
||||||
|
import { HorizontalLine } from './horizontal-line';
|
||||||
|
|
||||||
|
export class HorizontalLineAxisView implements ISeriesPrimitiveAxisView {
|
||||||
|
_source: HorizontalLine;
|
||||||
|
_y: Coordinate | null = null;
|
||||||
|
_price: string | null = null;
|
||||||
|
|
||||||
|
constructor(source: HorizontalLine) {
|
||||||
|
this._source = source;
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
if (!this._source.series || !this._source._point) return;
|
||||||
|
this._y = this._source.series.priceToCoordinate(this._source._point.price);
|
||||||
|
const priceFormat = this._source.series.options().priceFormat as PriceFormatBuiltIn;
|
||||||
|
const precision = priceFormat.precision;
|
||||||
|
this._price = this._source._point.price.toFixed(precision).toString();
|
||||||
|
}
|
||||||
|
visible() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
tickVisible() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
coordinate() {
|
||||||
|
return this._y ?? 0;
|
||||||
|
}
|
||||||
|
text() {
|
||||||
|
return this._source._options.text || this._price || '';
|
||||||
|
}
|
||||||
|
textColor() {
|
||||||
|
return 'white';
|
||||||
|
}
|
||||||
|
backColor() {
|
||||||
|
return this._source._options.lineColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import { Drawing, InteractionState } from "../drawing/drawing";
|
|||||||
import { DrawingOptions } from "../drawing/options";
|
import { DrawingOptions } from "../drawing/options";
|
||||||
import { HorizontalLinePaneView } from "./pane-view";
|
import { HorizontalLinePaneView } from "./pane-view";
|
||||||
import { GlobalParams } from "../general/global-params";
|
import { GlobalParams } from "../general/global-params";
|
||||||
|
import { HorizontalLineAxisView } from "./axis-view";
|
||||||
|
|
||||||
|
|
||||||
declare const window: GlobalParams;
|
declare const window: GlobalParams;
|
||||||
@ -16,6 +17,7 @@ export class HorizontalLine extends Drawing {
|
|||||||
_paneViews: HorizontalLinePaneView[];
|
_paneViews: HorizontalLinePaneView[];
|
||||||
_point: Point;
|
_point: Point;
|
||||||
private _callbackName: string | null;
|
private _callbackName: string | null;
|
||||||
|
_priceAxisViews: HorizontalLineAxisView[];
|
||||||
|
|
||||||
protected _startDragPoint: Point | null = null;
|
protected _startDragPoint: Point | null = null;
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ export class HorizontalLine extends Drawing {
|
|||||||
this._point = point;
|
this._point = point;
|
||||||
this._point.time = null; // time is null for horizontal lines
|
this._point.time = null; // time is null for horizontal lines
|
||||||
this._paneViews = [new HorizontalLinePaneView(this)];
|
this._paneViews = [new HorizontalLinePaneView(this)];
|
||||||
|
this._priceAxisViews = [new HorizontalLineAxisView(this)];
|
||||||
|
|
||||||
this._callbackName = callbackName;
|
this._callbackName = callbackName;
|
||||||
}
|
}
|
||||||
@ -37,6 +40,15 @@ export class HorizontalLine extends Drawing {
|
|||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAllViews() {
|
||||||
|
this._paneViews.forEach((pw) => pw.update());
|
||||||
|
this._priceAxisViews.forEach((tw) => tw.update());
|
||||||
|
}
|
||||||
|
|
||||||
|
priceAxisViews() {
|
||||||
|
return this._priceAxisViews;
|
||||||
|
}
|
||||||
|
|
||||||
_moveToState(state: InteractionState) {
|
_moveToState(state: InteractionState) {
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case InteractionState.NONE:
|
case InteractionState.NONE:
|
||||||
|
|||||||
35
src/vertical-line/axis-view.ts
Normal file
35
src/vertical-line/axis-view.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Coordinate, ISeriesPrimitiveAxisView } from "lightweight-charts";
|
||||||
|
import { VerticalLine } from "./vertical-line";
|
||||||
|
|
||||||
|
export class VerticalLineTimeAxisView implements ISeriesPrimitiveAxisView {
|
||||||
|
_source: VerticalLine;
|
||||||
|
_x: Coordinate | null = null;
|
||||||
|
|
||||||
|
constructor(source: VerticalLine) {
|
||||||
|
this._source = source;
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
if (!this._source.chart|| !this._source._point) return;
|
||||||
|
const point = this._source._point;
|
||||||
|
const timeScale = this._source.chart.timeScale();
|
||||||
|
this._x = point.time ? timeScale.timeToCoordinate(point.time) : timeScale.logicalToCoordinate(point.logical);
|
||||||
|
}
|
||||||
|
visible() {
|
||||||
|
return !!this._source._options.text;
|
||||||
|
}
|
||||||
|
tickVisible() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
coordinate() {
|
||||||
|
return this._x ?? 0;
|
||||||
|
}
|
||||||
|
text() {
|
||||||
|
return this._source._options.text || '';
|
||||||
|
}
|
||||||
|
textColor() {
|
||||||
|
return "white";
|
||||||
|
}
|
||||||
|
backColor() {
|
||||||
|
return this._source._options.lineColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ export class VerticalLinePaneView extends DrawingPaneView {
|
|||||||
const point = this._source._point;
|
const point = this._source._point;
|
||||||
const timeScale = this._source.chart.timeScale()
|
const timeScale = this._source.chart.timeScale()
|
||||||
const series = this._source.series;
|
const series = this._source.series;
|
||||||
this._point.x = timeScale.logicalToCoordinate(point.logical)
|
this._point.x = point.time ? timeScale.timeToCoordinate(point.time) : timeScale.logicalToCoordinate(point.logical)
|
||||||
this._point.y = series.priceToCoordinate(point.price);
|
this._point.y = series.priceToCoordinate(point.price);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { Drawing, InteractionState } from "../drawing/drawing";
|
|||||||
import { DrawingOptions } from "../drawing/options";
|
import { DrawingOptions } from "../drawing/options";
|
||||||
import { VerticalLinePaneView } from "./pane-view";
|
import { VerticalLinePaneView } from "./pane-view";
|
||||||
import { GlobalParams } from "../general/global-params";
|
import { GlobalParams } from "../general/global-params";
|
||||||
|
import { VerticalLineTimeAxisView } from "./axis-view";
|
||||||
|
|
||||||
|
|
||||||
declare const window: GlobalParams;
|
declare const window: GlobalParams;
|
||||||
@ -14,6 +15,7 @@ declare const window: GlobalParams;
|
|||||||
export class VerticalLine extends Drawing {
|
export class VerticalLine extends Drawing {
|
||||||
_type = 'VerticalLine';
|
_type = 'VerticalLine';
|
||||||
_paneViews: VerticalLinePaneView[];
|
_paneViews: VerticalLinePaneView[];
|
||||||
|
_timeAxisViews: VerticalLineTimeAxisView[];
|
||||||
_point: Point;
|
_point: Point;
|
||||||
private _callbackName: string | null;
|
private _callbackName: string | null;
|
||||||
|
|
||||||
@ -24,10 +26,27 @@ export class VerticalLine extends Drawing {
|
|||||||
this._point = point;
|
this._point = point;
|
||||||
this._paneViews = [new VerticalLinePaneView(this)];
|
this._paneViews = [new VerticalLinePaneView(this)];
|
||||||
this._callbackName = callbackName;
|
this._callbackName = callbackName;
|
||||||
|
|
||||||
|
this._timeAxisViews = [new VerticalLineTimeAxisView(this)]
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAllViews() {
|
||||||
|
this._paneViews.forEach(pw => pw.update());
|
||||||
|
this._timeAxisViews.forEach(tw => tw.update());
|
||||||
|
}
|
||||||
|
|
||||||
|
timeAxisViews() {
|
||||||
|
return this._timeAxisViews;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updatePoints(...points: (Point | null)[]) {
|
public updatePoints(...points: (Point | null)[]) {
|
||||||
for (const p of points) if (p) this._point = p;
|
for (const p of points) {
|
||||||
|
if (!p) continue;
|
||||||
|
if (!p.time && p.logical) {
|
||||||
|
p.time = this.series.dataByIndex(p.logical)?.time || null
|
||||||
|
}
|
||||||
|
this._point = p;
|
||||||
|
}
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user