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


.legend {
  display: flex;
  width: 100%;
  flex-direction: column;
  gap: var(--space-0250);
  color: var(--neutral-12);

  .row {
    display: flex;
    justify-content: start;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-0500);
  }

  .item {
    display: flex;
    gap: var(--space-0250);
  }

  .item,
  .symbol {
    font-size: var(--space-0750);
    line-height: var(--space-1000);
    font-weight: var(--font-medium);
  }
}



import { Observable } from 'rxjs';

import { LegendSeriesRow } from '@src/components/LegendSeriesRow';

import { IndicatorsIds } from '@src/core/Indicators';

import { CompareMode, LegendVM, OHLCConfig } from '../../types';
import { formatPrice, getPriceColor, useObservable } from '../../utils';

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

export interface LegendProps {
  ohlcConfig?: OHLCConfig;
  viewModel: Observable<LegendVM>;
  onCompareRemove?: (symbol: string, mode: CompareMode) => void;
  onIndicatorEdit?: (indicator: IndicatorsIds) => void;
  onIndicatorRemove?: (indicatorId: IndicatorsIds) => void;
}

export const LegendComponent = ({
  ohlcConfig,
  viewModel: vm,
  onCompareRemove,
  onIndicatorRemove,
  onIndicatorEdit,
}: LegendProps) => {
  const viewModel = useObservable(vm);
  const ohlc = viewModel?.ohlc ?? null;
  const symbol = viewModel?.symbol ?? null;
  const compareItems = viewModel?.compareItems ?? [];
  const indicatorItems = viewModel?.indicatorItems ?? [];

  const showOhlc = ohlcConfig?.show;

  const priceColor = getPriceColor(ohlc);

  const renderLegendItem = (label: string, value?: string | number, show = true) => {
    if (!show || !value) return null;

    return (
      <div className={styles.item}>
        {label} <span style={{ color: priceColor }}>{value}</span>
      </div>
    );
  };

  return (
    <section className={styles.legend}>
      <div className={styles.row}>
        {symbol && <div className={styles.symbol}>{symbol}</div>}
        {showOhlc && (
          <>
            {renderLegendItem('Отк.', formatPrice(ohlc?.open), !!ohlc?.open)}
            {renderLegendItem('Макс.', formatPrice(ohlc?.high), !!ohlc?.high)}
            {renderLegendItem('Мин.', formatPrice(ohlc?.low), !!ohlc?.low)}
            {renderLegendItem('Закр.', formatPrice(ohlc?.close), !!ohlc?.close)}
            {renderLegendItem(
              'Изм.',
              `${formatPrice(ohlc?.percentageChange)}%`,
              !!ohlc?.absoluteChange && !!ohlc?.percentageChange,
            )}
          </>
        )}
      </div>
      {indicatorItems.map(({ id, label, values }) => (
        <LegendSeriesRow
          key={id}
          label={label}
          values={values}
          onEdit={() => onIndicatorEdit?.(id)}
          onRemove={() => onIndicatorRemove?.(id)}
        />
      ))}
      {compareItems.map(({ symbol: itemSymbol, mode, values }) => (
        <LegendSeriesRow
          key={`${itemSymbol}|${mode}`}
          label={itemSymbol}
          values={values}
          onRemove={() => onCompareRemove?.(itemSymbol, mode)}
        />
      ))}
    </section>
  );
};



.item {
  width: 100%;
  max-width: max-content;
  display: flex;
  align-items: center;
  gap: var(--space-0500);

  .priceWrapper {
    position: relative;
    display: flex;
    justify-content: start;
    align-items: center;
    gap: var(--space-0500);
  }

  .symbol,
  .price {
    font-size: var(--space-0750);
    font-weight: var(--font-medium);
  }

  .button {
    display: none;
    width: var(--space-1000);
    height: var(--space-1000);
    border-radius: var(--space-0125);
    color: var(--neutral-13);
    padding: 0;
    min-width: 0;
    opacity: 0;
    pointer-events: none;

    & p {
      display: flex;
      justify-content: center;
      align-items: start;
      padding: 0;

      svg {
        width: var(--space-0875);
        height: var(--space-0875);
      }
    }
  }

  &:hover .button {
    display: block;
    opacity: 1;
    pointer-events: auto;
  }

  &:hover .price {
    display: none;
    opacity: 0;
    pointer-events: none;
  }
}



import { Button } from 'exchange-elements/v2';

import { GearIcon, TrashIcon } from '@src/components/Icon';

import { LegendValueItem } from '@src/types';

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

interface LegendSeriesRowProps {
  label: string;
  values?: LegendValueItem[];
  onRemove?: () => void;
  onEdit?: () => void;
}

export function LegendSeriesRow({ label, values, onRemove, onEdit }: LegendSeriesRowProps) {
  return (
    <div className={styles.item}>
      <div className={styles.symbol}>{label}</div>

      <div className={styles.priceWrapper}>
        {values?.map(({ id, value, color }) => (
          <span
            key={id}
            style={{ color }}
            className={styles.price}
          >
            {value}
          </span>
        ))}

        {onEdit && (
          <Button
            size="sm"
            className={styles.button}
            onClick={onEdit}
            label={<GearIcon />}
          />
        )}

        {onRemove && (
          <Button
            size="sm"
            className={styles.button}
            onClick={onRemove}
            label={<TrashIcon />}
          />
        )}
      </div>
    </div>
  );
}