Загрузка данных


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,
    });
  }
}