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


import { getThemeStore } from '@src/theme';
import { SettingField, SettingsTab, SettingsValues } from '@src/types';

export interface TrendLineStyle {
  lineColor: string;
}

export interface TrendLineTextStyle {
  text: string;
  fontSize: number;
  isBold: boolean;
  isItalic: boolean;
  textColor: string;
}

export type TrendLineSettings = TrendLineStyle & TrendLineTextStyle & SettingsValues;

export function createDefaultSettings(): TrendLineSettings {
  const { colors } = getThemeStore();

  return {
    lineColor: colors.chartLineColor,

    text: '',
    fontSize: 14,
    isBold: false,
    isItalic: false,
    textColor: colors.chartPriceLineText,
  };
}

export function getTrendLineSettingsTabs(settings: TrendLineSettings): SettingsTab[] {
  const styleFields: SettingField[] = [
    {
      key: 'lineColor',
      label: 'Цвет линии',
      type: 'color',
      defaultValue: settings.lineColor,
    },
  ];

  const textFields: SettingField[] = [
    {
      key: 'textColor',
      label: 'Цвет текста',
      type: 'color',
      defaultValue: settings.textColor,
    },
    {
      key: 'fontSize',
      label: 'Размер текста',
      type: 'number',
      defaultValue: settings.fontSize,
      min: 8,
      max: 24,
    },
    {
      key: 'isBold',
      label: 'Жирный текст',
      type: 'boolean',
      defaultValue: settings.isBold,
    },
    {
      key: 'isItalic',
      label: 'Курсив',
      type: 'boolean',
      defaultValue: settings.isItalic,
    },
    {
      key: 'text',
      label: 'Текст',
      type: 'textarea',
      defaultValue: settings.text,
      placeholder: 'Введите текст',
    },
  ];

  return [
    {
      key: 'style',
      label: 'Стиль',
      fields: styleFields,
    },
    {
      key: 'text',
      label: 'Текст',
      fields: textFields,
    },
  ];
}



import { CanvasRenderingTarget2D } from 'fancy-canvas';
import { IPrimitivePaneRenderer } from 'lightweight-charts';

import { getThemeStore } from '@src/theme';

import { TrendLine } from './trendLine';

const UI = {
  lineWidth: 2,
  handleRadius: 5,
  handleBorderWidth: 2,
  textOffset: 8,
  textLineHeightMultiplier: 1.2,
};

export class TrendLinePaneRenderer implements IPrimitivePaneRenderer {
  private readonly trendLine: TrendLine;

  constructor(trendLine: TrendLine) {
    this.trendLine = trendLine;
  }

  public draw(target: CanvasRenderingTarget2D): void {
    const data = this.trendLine.getRenderData();

    if (!data) {
      return;
    }

    const { colors } = getThemeStore();

    target.useBitmapCoordinateSpace(({ context, horizontalPixelRatio, verticalPixelRatio }) => {
      const pixelRatio = Math.max(horizontalPixelRatio, verticalPixelRatio);

      const startX = data.startPoint.x * horizontalPixelRatio;
      const startY = data.startPoint.y * verticalPixelRatio;
      const endX = data.endPoint.x * horizontalPixelRatio;
      const endY = data.endPoint.y * verticalPixelRatio;

      context.save();

      context.lineWidth = UI.lineWidth * pixelRatio;
      context.strokeStyle = data.lineColor;
      context.beginPath();
      context.moveTo(startX, startY);
      context.lineTo(endX, endY);
      context.stroke();

      if (data.text.trim()) {
        drawText(
          context,
          (startX + endX) / 2,
          (startY + endY) / 2,
          data.text,
          data.fontSize,
          data.isBold,
          data.isItalic,
          data.textColor,
          verticalPixelRatio,
        );
      }

      if (data.showHandles) {
        context.fillStyle = colors.chartBackground;
        context.strokeStyle = colors.chartLineColor;
        context.lineWidth = UI.handleBorderWidth * pixelRatio;

        drawHandle(context, startX, startY, UI.handleRadius * pixelRatio);
        drawHandle(context, endX, endY, UI.handleRadius * pixelRatio);
      }

      context.restore();
    });
  }
}

function drawHandle(context: CanvasRenderingContext2D, x: number, y: number, radius: number): void {
  context.beginPath();
  context.arc(x, y, radius, 0, Math.PI * 2);
  context.fill();
  context.stroke();
}

function drawText(
  context: CanvasRenderingContext2D,
  centerX: number,
  centerY: number,
  text: string,
  fontSize: number,
  isBold: boolean,
  isItalic: boolean,
  textColor: string,
  verticalPixelRatio: number,
): void {
  const lines = text.split('\n');
  const safeFontSize = Math.max(1, fontSize);
  const fontSizePx = safeFontSize * verticalPixelRatio;
  const lineHeight = safeFontSize * UI.textLineHeightMultiplier * verticalPixelRatio;

  const fontWeight = isBold ? '700 ' : '';
  const fontStyle = isItalic ? 'italic ' : '';

  context.save();

  context.font = `${fontStyle}${fontWeight}${fontSizePx}px Inter, sans-serif`;
  context.fillStyle = textColor;
  context.textAlign = 'center';
  context.textBaseline = 'middle';

  const textBlockHeight = lines.length * lineHeight;
  const firstLineY = centerY - textBlockHeight / 2 + lineHeight / 2 - UI.textOffset * verticalPixelRatio;

  lines.forEach((line, index) => {
    context.fillText(line, centerX, firstLineY + index * lineHeight);
  });

  context.restore();
}