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


import { memo, useState } from 'react';
import { InputText } from 'exchange-elements/v2';

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

interface TextFieldProps {
  label: string;
  initialValue: string;
  onValueChange: (value: string) => void;
  normalizeValue?: (value: string) => string;
}

export const TextField = memo(function TextField({
  label,
  initialValue,
  onValueChange,
  normalizeValue,
}: TextFieldProps) {
  const [value, setValue] = useState(initialValue);

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const rawValue = event.currentTarget.value;
    const nextValue = normalizeValue ? normalizeValue(rawValue) : rawValue;

    setValue(nextValue);
    onValueChange(nextValue);
  };

  return (
    <InputText
      className={styles.input}
      value={value}
      onChange={handleChange}
      label={label}
      labelPos="top"
      size="sm"
    />
  );
});


import { useRef } from 'react';
import { Flex } from 'exchange-elements/v2';

import { TextField, SelectField } from '@src/components/Fields';
import { MaIndicatorSettings, MaSource } from '@src/core/Indicators/settings';

interface IndicatorSettingsModalProps {
  settings: MaIndicatorSettings;
  onSettingsChange?: (settings: MaIndicatorSettings) => void;
}

const sourceOptions: { label: string; value: MaSource }[] = [
  { label: 'Цена открытия', value: 'open' },
  { label: 'Максимум', value: 'high' },
  { label: 'Минимум', value: 'low' },
  { label: 'Цена закрытия', value: 'close' },
];

export function IndicatorSettingsModal({ settings, onSettingsChange }: IndicatorSettingsModalProps) {
  const draftSettingsRef = useRef({ ...settings });

  const emitSettingsChange = () => {
    onSettingsChange?.({ ...draftSettingsRef.current });
  };

  const normalizeLengthValue = (value: string) => value.replace(/[^\d]/g, '');

  const normalizeOffsetValue = (value: string) => value.replace(/[^\d-]/g, '');

  const handleLengthValueChange = (nextLengthValue: string) => {
    const nextLength = Number(nextLengthValue);

    draftSettingsRef.current = {
      ...draftSettingsRef.current,
      length: Number.isFinite(nextLength) && nextLength > 0 ? nextLength : 1,
    };

    emitSettingsChange();
  };

  const handleSourceValueChange = (nextSource: string) => {
    draftSettingsRef.current = {
      ...draftSettingsRef.current,
      source: nextSource as MaSource,
    };

    emitSettingsChange();
  };

  const handleOffsetValueChange = (nextOffsetValue: string) => {
    const nextOffset = Number(nextOffsetValue);

    draftSettingsRef.current = {
      ...draftSettingsRef.current,
      offset: Number.isFinite(nextOffset) ? nextOffset : 0,
    };

    emitSettingsChange();
  };

  return (
    <Flex
      direction="column"
      divider={null}
      spacing="0500"
      wrap="nowrap"
    >
      <TextField
        label="Длина"
        initialValue={String(settings.length)}
        normalizeValue={normalizeLengthValue}
        onValueChange={handleLengthValueChange}
      />

      <SelectField
        label="Данные"
        initialValue={settings.source}
        options={sourceOptions}
        onValueChange={handleSourceValueChange}
      />

      <TextField
        label="Отступ"
        initialValue={String(settings.offset)}
        normalizeValue={normalizeOffsetValue}
        onValueChange={handleOffsetValueChange}
      />
    </Flex>
  );
}