import {
BaselineSeries,
type IPriceLine,
LineStyle,
type SeriesDefinition,
type SeriesPartialOptionsMap,
type SingleValueData,
type Time,
} from 'lightweight-charts';
import type { BaseSeriesParams } from '@core/Series/BaseSeries';
import { SingleValueSeriesStrategy } from '@core/Series/SingleValueSeriesStrategy';
import { getThemeStore } from '@src/theme/store';
const BASE_LEVEL = 0.5;
export class BaselineSeriesStrategy extends SingleValueSeriesStrategy<'Baseline'> {
private basePriceLine: IPriceLine | null = null;
private updateFrame: number | null = null;
constructor(params: BaseSeriesParams<'Baseline'>) {
super(params);
const timeScale = this.lwcChart.timeScale();
timeScale.subscribeVisibleLogicalRangeChange(this.scheduleBaseValueUpdate);
timeScale.subscribeSizeChange(this.scheduleBaseValueUpdate);
this.subscribeDataChanged(this.scheduleBaseValueUpdate);
this.scheduleBaseValueUpdate();
}
public destroy(): void {
const timeScale = this.lwcChart.timeScale();
timeScale.unsubscribeVisibleLogicalRangeChange(this.scheduleBaseValueUpdate);
timeScale.unsubscribeSizeChange(this.scheduleBaseValueUpdate);
this.unsubscribeDataChanged(this.scheduleBaseValueUpdate);
if (this.updateFrame !== null) {
cancelAnimationFrame(this.updateFrame);
this.updateFrame = null;
}
if (this.basePriceLine) {
this.removePriceLine(this.basePriceLine);
this.basePriceLine = null;
}
super.destroy();
}
protected seriesDefinition(): SeriesDefinition<'Baseline'> {
return BaselineSeries;
}
protected getDefaultOptions(): SeriesPartialOptionsMap['Baseline'] {
return {
relativeGradient: true,
};
}
protected getLegendColor(currentBar: SingleValueData<Time>): string {
const options = this.options();
return currentBar.value >= options.baseValue.price
? options.topLineColor
: options.bottomLineColor;
}
public getTypeName(): string {
return 'Baseline';
}
private scheduleBaseValueUpdate = (): void => {
if (this.updateFrame !== null) {
cancelAnimationFrame(this.updateFrame);
}
this.updateFrame = requestAnimationFrame(() => {
this.updateFrame = null;
this.updateBaseValue();
});
};
private updateBaseValue(): void {
const paneHeight = this.getPane().getHeight();
if (paneHeight <= 0) {
return;
}
const basePrice = this.coordinateToPrice(paneHeight * BASE_LEVEL);
if (basePrice === null) {
return;
}
this.applyOptions({
baseValue: {
type: 'price',
price: basePrice,
},
});
this.updateBasePriceLine(basePrice);
}
private updateBasePriceLine(price: number): void {
if (this.basePriceLine) {
this.basePriceLine.applyOptions({
price,
});
return;
}
const { colors } = getThemeStore();
this.basePriceLine = this.createPriceLine({
price,
color: colors.chartCrosshairLine,
lineWidth: 1,
lineStyle: LineStyle.Dashed,
axisLabelVisible: false,
});
}
}