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


import classNames from 'classnames';
import { ChangeEvent, useId } from 'react';

import styles from './index.module.scss';

interface RangeFieldProps {
  label?: string;
  value: number;
  min?: number;
  max?: number;
  step?: number;
  color?: string;
  suffix?: string;
  size?: 'sm' | 'md';
  onChange: (event: ChangeEvent<HTMLInputElement>) => void;
}

export const RangeField = ({
  label,
  value,
  min = 0,
  max = 100,
  step = 1,
  color = 'var(--blue-1)',
  suffix = '%',
  size = 'md',
  onChange,
}: RangeFieldProps) => {
  const inputId = useId();

  return (
    <div className={classNames(styles.range, { [styles.range_sm]: size === 'sm' })}>
      {label && (
        <label
          htmlFor={inputId}
          className={styles.range_label}
        >
          {label}
        </label>
      )}

      <div className={styles.range_wrapper}>
        <input
          id={inputId}
          type="range"
          min={min}
          max={max}
          step={step}
          value={value}
          className={styles.range_input}
          style={{ ['--range-color' as string]: color }}
          onChange={onChange}
        />

        <span className={styles.range_value}>
          {value}
          {suffix}
        </span>
      </div>
    </div>
  );
};




@use '../../theme/mixins' as m;

[data-theme='mxt'],
[data-theme='mb'][data-mode='light'] {
  --dd-item-text-selected: var(--neutral-2);
  --dd-item-bg-selected: var(--neutral-12);
  --color-field-border: var(--neutral-11);
  --color-field-square: #00000024;
}

[data-theme='tr'],
[data-theme='mb'][data-mode='dark'] {
  --dd-item-text-selected: var(--neutral-13);
  --dd-item-bg-selected: var(--neutral-5);
  --color-field-border: var(--neutral-9);
  --color-field-square: #ffffff14;
}

$button-color-bottom: 5px;

.input {
  color: var(--neutral-12);

  input {
    color: var(--neutral-12);
  }

  &_select {
    &:focus-within {
      background-color: transparent !important;
    }

    div {
      background-color: transparent;

      &:focus-within {
        background-color: transparent !important;
      }

      .input {
        background-color: transparent;
      }
    }
  }

  &_color {
    display: flex;
    flex-direction: column;
    gap: var(--space-0250);

    .input_wrapper {
      position: relative;
      width: 100%;
    }
  }
}

.dropdown {
  li {
    &[aria-selected='true'] {
      background-color: var(--dd-item-bg-selected);

      div[aria-label='Label'] {
        color: var(--dd-item-text-selected);
      }
    }

    svg {
      color: transparent;
    }
  }
}

.button_color {
  position: absolute;
  bottom: $button-color-bottom;
  right: var(--space-0500);
  width: var(--space-1375);
  height: var(--space-1375);
  border: 1px solid var(--color-field-border);
  border-radius: var(--space-0125);
}

.input_checkbox {
  padding: var(--space-0375) 0 !important;

  span {
    font-size: var(--space-0875);
    line-height: normal;
  }
}

.input_textarea {
  textarea {
    font-size: var(--space-0875);
    line-height: normal;
  }
}

.range {
  --range-input-height: var(--space-1000);
  --range-track-height: var(--space-0750);
  --range-thumb-size: var(--space-1000);
  --range-pattern-size: var(--space-0500);
  --range-label-font-size: var(--space-0875);
  --range-value-font-size: var(--space-0750);
  --range-wrapper-gap: var(--space-0500);

  display: flex;
  flex-direction: column;
  gap: var(--space-0250);

  &_sm {
    --range-input-height: var(--space-0875);
    --range-track-height: var(--space-0500);
    --range-thumb-size: var(--space-0875);
    --range-pattern-size: var(--space-0375);
    --range-label-font-size: var(--space-0750);
    --range-wrapper-gap: var(--space-0375);
  }

  &_label {
    color: var(--neutral-12);
    font-size: var(--range-label-font-size);
  }

  &_wrapper {
    display: grid;
    align-items: center;
    grid-template: 1fr / 1fr auto;
    gap: var(--range-wrapper-gap);
  }

  &_value {
    min-width: var(--space-2000);
    color: var(--neutral-12);
    font-size: var(--range-value-font-size);
    line-height: normal;
    text-align: right;
  }

  &_input {
    --range-color: var(--blue-1);

    width: 100%;
    height: var(--range-input-height);
    appearance: none;
    background: transparent;
    cursor: pointer;

    &::-webkit-slider-runnable-track {
      height: var(--range-track-height);
      border: 1px solid var(--color-field-border);
      border-radius: 999px;
      background: linear-gradient(to right, transparent 0%, var(--range-color) 100%),
        linear-gradient(45deg, var(--color-field-square) 25%, transparent 25%),
        linear-gradient(-45deg, var(--color-field-square) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, var(--color-field-square) 75%),
        linear-gradient(-45deg, transparent 75%, var(--color-field-square) 75%);
      background-size:
        100% 100%,
        var(--range-pattern-size) var(--range-pattern-size),
        var(--range-pattern-size) var(--range-pattern-size),
        var(--range-pattern-size) var(--range-pattern-size),
        var(--range-pattern-size) var(--range-pattern-size);
      background-position:
        0 0,
        0 0,
        0 calc(var(--range-pattern-size) / 2),
        calc(var(--range-pattern-size) / 2) calc(var(--range-pattern-size) / -2),
        calc(var(--range-pattern-size) / -2) 0;
    }

    &::-webkit-slider-thumb {
      width: var(--range-thumb-size);
      height: var(--range-thumb-size);
      margin-top: calc((var(--range-track-height) - var(--range-thumb-size)) / 2 - 1px);
      appearance: none;
      border: 1px solid var(--color-field-border);
      border-radius: 50%;
      background-color: var(--neutral-panel);
    }

    &::-moz-range-track {
      height: var(--range-track-height);
      border: 1px solid var(--color-field-border);
      border-radius: 999px;
      background: linear-gradient(to right, transparent 0%, var(--range-color) 100%),
        linear-gradient(45deg, var(--color-field-square) 25%, transparent 25%),
        linear-gradient(-45deg, var(--color-field-square) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, var(--color-field-square) 75%),
        linear-gradient(-45deg, transparent 75%, var(--color-field-square) 75%);
      background-size:
        100% 100%,
        var(--range-pattern-size) var(--range-pattern-size),
        var(--range-pattern-size) var(--range-pattern-size),
        var(--range-pattern-size) var(--range-pattern-size),
        var(--range-pattern-size) var(--range-pattern-size);
      background-position:
        0 0,
        0 0,
        0 calc(var(--range-pattern-size) / 2),
        calc(var(--range-pattern-size) / 2) calc(var(--range-pattern-size) / -2),
        calc(var(--range-pattern-size) / -2) 0;
    }

    &::-moz-range-progress {
      background: transparent;
    }

    &::-moz-range-thumb {
      width: var(--range-thumb-size);
      height: var(--range-thumb-size);
      border: 1px solid var(--color-field-border);
      border-radius: 50%;
      background-color: var(--neutral-panel);
    }
  }
}

.color {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1000);

  &_label {
    font-size: var(--space-0875);
    color: var(--neutral-12);
    font-weight: var(--font-medium);
  }

  &_menu {
    --dropdown-width: max-content;
    padding: var(--space-1000);
  }

  &_trigger {
    min-width: none;
    width: var(--space-2000);
    height: var(--space-2000);
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: var(--space-0375);
    border: var(--borderWidth-md) solid var(--neutral-9);

    p {
      padding: 0;
    }
  }

  &_preview {
    width: var(--space-1375);
    height: var(--space-1375);
    border-radius: var(--space-0250);
  }
}

















import { DrawingsManagerSnapshot } from '@core/DrawingsManager';
import { ChartSeriesType, DateFormat, IndicatorsIds, Intervals, Timeframes } from '@lib';
import { Direction } from '@src/types/chart';
import { MainSymbolSnapshotInput, SymbolInfo, SymbolInfoInput } from '@src/types/symbol';
import { TimeFormat } from '@src/types/timeScale';

import { SettingsValues } from './settings';

import type { PriceScaleMode } from 'lightweight-charts';

export type PriceScaleSide = Direction.Left | Direction.Right;

export interface ISerializable<T extends object> {
  getSnapshot: () => T;
}

export interface InitialSnapshot extends MainSymbolSnapshotInput {
  timeframe: Timeframes; // todo: move to snap
  chartSeriesType: ChartSeriesType; // todo: move to snap
}

export interface MoexChartSnapshotInput {
  // settings: ChartSettingsSnapshot;
  charts: ChartSnapshotInput[];
}

export interface MoexChartSnapshot {
  // settings: ChartSettingsSnapshot;
  charts: ChartSnapshot[];
}

interface ChartSnapshotBase {
  timeframe: Timeframes;
  chartSeriesType: ChartSeriesType;
  timeFormat?: TimeFormat;
  dateFormat?: DateFormat;
  interval?: Intervals | null;
  panes: PaneSnapshot[];
}

export interface ChartSnapshotInput extends ChartSnapshotBase, MainSymbolSnapshotInput {}
export interface ChartSnapshot extends ChartSnapshotBase, SymbolInfo {}

export interface PriceScaleSnapshot {
  side: PriceScaleSide;
  mode: PriceScaleMode;
}

export interface PaneSnapshot {
  isMain: boolean;
  id: number;
  indicators: (IndicatorSnapshot | CompareSnapshot)[];
  drawings: DrawingsManagerSnapshot;
  priceScales?: PriceScaleSnapshot[];
}

export interface CompareSnapshot extends Partial<DOMObjectSnapshot> {
  symbolInfo: SymbolInfoInput;
  seriesName: ChartSeriesType;
  scale: Direction;
}

export interface IndicatorSnapshot extends Partial<DOMObjectSnapshot> {
  indicatorType: IndicatorsIds;
  settings?: SettingsValues;
}

// todo: move DrawingsManagerSnapshot here

export interface DOMObjectSnapshot {
  id: string;
  name: string;
  zIndex: number;
  hidden: boolean;
  paneId: number;
}

















export interface SymbolInfo {
  symbolId: string;
  symbol: string;
  symbolName: string;
}

export interface SymbolInfoInput {
  symbolId: string;
  symbol?: string;
  symbolName?: string;
}

export interface MainSymbolSnapshotInput extends Omit<SymbolInfoInput, 'symbolId'> {
  symbolId: string | null;
}

















import { SeriesType, Time } from 'lightweight-charts';

import { DateFormat } from '@src/utils/formatter';

import { TimeFormat } from './timeScale';
/**
 * Типы графиков, поддерживаемые библиотекой
 */
export type ChartSeriesType = SeriesType;

/**
 * Универсальный формат данных для всех типов графиков
 */
export interface ChartDataPoint {
  time?: Time;
  value?: number;
  open?: number;
  high?: number;
  low?: number;
  close?: number;
  volume?: number;
}

interface CandlestickChartOptions {
  upColor?: string;
  downColor?: string;
  wickUpColor?: string;
  wickDownColor?: string;
  borderVisible?: boolean;
}

interface LineChartOptions {
  lineStyle?: 'solid' | 'dotted' | 'dashed';
}

interface AreaChartOptions {
  topColor?: string;
  bottomColor?: string;
}

interface BaselineChartOptions {
  baseValue?: number;
  topLineColor?: string;
  bottomLineColor?: string;
  topFillColor1?: string;
  topFillColor2?: string;
  bottomFillColor1?: string;
  bottomFillColor2?: string;
}

interface TimescaleOptions {
  timeVisible?: boolean;
  secondsVisible?: boolean;
  timeFormat?: TimeFormat;
  dateFormat?: DateFormat;
}

interface CrosshairOptions {
  crosshairLineXColor?: string;
  crosshairLineYColor?: string;
  crosshairLabelXBgColor?: string;
  crosshairLabelYBgColor?: string;
}

interface HistogramOptions {
  scaleMargins?: {
    top: number;
    bottom: number;
  };
}

/**
 * @deprecated
 * Опции для разных типов графиков
 */
export interface ChartTypeOptions // todo: неверный интерфейс
  extends HistogramOptions,
    CrosshairOptions,
    TimescaleOptions,
    BaselineChartOptions,
    AreaChartOptions,
    LineChartOptions,
    CandlestickChartOptions {
  // Общие опции
  color?: string;
  lineWidth?: number;
}

export interface LineCandle {
  time: number;
  value: number;
  volume?: number;
}

export interface Candle {
  time: number;
  open: number;
  high: number;
  low: number;
  close: number;
  volume?: number; // todo: volume & future metaData to customFields
}

export type Destroyable = {
  destroy(): void;
};

export enum Direction {
  Top = 'top',
  Bottom = 'bottom',
  Left = 'left',
  Right = 'right',
}

// Настройки графика, которые могут изменяться в рантайме
export interface ChartOptionsModel {
  dateFormat: DateFormat;
  timeFormat: TimeFormat;
  showTime: boolean;
}