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


import { useEffect, useState } from 'react';

import { ColorField, NumberField, SelectField, TextAreaField, TextField } from '@src/components/FormFields';

import type { DrawingStyleField, DrawingStyleSettings } from '@src/core/Drawings/styleSettings';

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

interface DrawingStyleSettingsModalProps {
  fields: DrawingStyleField[];
  values: DrawingStyleSettings;
  onChange: (settings: DrawingStyleSettings) => void;
}

export const DrawingStyleSettingsModal = ({ fields, values, onChange }: DrawingStyleSettingsModalProps) => {
  const [form, setForm] = useState<DrawingStyleSettings>(values);

  useEffect(() => {
    setForm(values);
  }, [values]);

  const changeValue = (key: string, value: string | number | boolean) => {
    setForm((current) => {
      const next = {
        ...current,
        [key]: value,
      };

      onChange(next);
      return next;
    });
  };

  return (
    <div className={styles.form}>
      {fields.map((field) => {
        const value = form[field.key] ?? field.defaultValue;

        if (field.type === 'select') {
          return (
            <SelectField
              key={field.key}
              label={field.label}
              value={String(value)}
              options={field.options}
              onValueChange={(nextValue) => {
                changeValue(field.key, nextValue);
              }}
            />
          );
        }

        if (field.type === 'color') {
          return (
            <ColorField
              key={field.key}
              label={field.label}
              value={String(value)}
              onValueChange={(nextValue) => {
                changeValue(field.key, nextValue);
              }}
            />
          );
        }

        if (field.type === 'text') {
          return (
            <TextField
              key={field.key}
              label={field.label}
              value={String(value)}
              placeholder={field.placeholder}
              onValueChange={(nextValue) => {
                changeValue(field.key, nextValue);
              }}
            />
          );
        }

        if (field.type === 'textarea') {
          return (
            <TextAreaField
              key={field.key}
              label={field.label}
              value={String(value)}
              placeholder={field.placeholder}
              onValueChange={(nextValue) => {
                changeValue(field.key, nextValue);
              }}
            />
          );
        }

        if (field.type === 'boolean') {
          return (
            <label
              key={field.key}
              className={styles.checkboxField}
            >
              <input
                type="checkbox"
                checked={Boolean(value)}
                onChange={(event) => {
                  changeValue(field.key, event.target.checked);
                }}
              />
              <span>{field.label}</span>
            </label>
          );
        }

        return (
          <NumberField
            key={field.key}
            label={field.label}
            value={typeof value === 'number' ? value : field.defaultValue}
            min={field.min}
            max={field.max}
            onValueChange={(nextValue) => {
              changeValue(field.key, nextValue);
            }}
          />
        );
      })}
    </div>
  );
};



import { useMemo, useRef } from 'react';

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

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

interface ColorFieldProps {
  label: string;
  value: string;
  onValueChange: (value: string) => void;
}

function normalizeHex(value: string): string {
  const next = value.trim();

  if (!next) {
    return '#000000';
  }

  const withHash = next.startsWith('#') ? next : `#${next}`;

  if (/^#[0-9A-Fa-f]{6}$/.test(withHash)) {
    return withHash;
  }

  return '#000000';
}

export const ColorField = ({ label, value, onValueChange }: ColorFieldProps) => {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const normalizedValue = useMemo(() => normalizeHex(value), [value]);

  return (
    <div className={styles.field}>
      <span className={styles.label}>{label}</span>

      <div className={styles.colorFieldRow}>
        <button
          type="button"
          className={styles.colorPreview}
          style={{ backgroundColor: normalizedValue }}
          onClick={() => inputRef.current?.click()}
        />

        <input
          ref={inputRef}
          type="color"
          className={styles.hiddenColorInput}
          value={normalizedValue}
          onChange={(event) => {
            onValueChange(event.target.value);
          }}
        />

        <InputText
          value={normalizedValue}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            onValueChange(event.target.value);
          }}
        />
      </div>
    </div>
  );
};


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

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

interface TextAreaFieldProps {
  label: string;
  value: string;
  placeholder?: string;
  onValueChange: (value: string) => void;
}

export const TextAreaField = ({ label, value, placeholder, onValueChange }: TextAreaFieldProps) => {
  return (
    <label className={styles.field}>
      <span className={styles.label}>{label}</span>
      <InputTextArea
        value={value}
        placeholder={placeholder}
        onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => {
          onValueChange(event.target.value);
        }}
      />
    </label>
  );
};


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

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

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

export const TextField = ({ label, value, placeholder, onValueChange }: TextFieldProps) => {
  return (
    <label className={styles.field}>
      <span className={styles.label}>{label}</span>
      <InputText
        value={value}
        placeholder={placeholder}
        onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
          onValueChange(event.target.value);
        }}
      />
    </label>
  );
};