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


import { useEffect, useMemo, useState } from 'react';

import { ColorField, NumberField, SelectField, TextAreaField, TextField } from '@src/components/FormFields';
import { DrawingStyleField } from '@src/core/Drawings/types';
import { IndicatorSetting } from '@src/types';

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

type SettingsValue = string | number | boolean;
type SettingsValues = Record<string, SettingsValue>;
type SettingsField = IndicatorSetting | DrawingStyleField;

interface SettingsTab {
  key: string;
  label: string;
  fields: SettingsField[];
}

interface EntitySettingsModalProps {
  tabs: SettingsTab[];
  values: SettingsValues;
  onChange: (settings: SettingsValues) => void;
  initialTabKey?: string;
}

export const EntitySettingsModal = ({
  tabs,
  values,
  onChange,
  initialTabKey,
}: EntitySettingsModalProps) => {
  const availableTabs = useMemo(() => tabs.filter((tab) => tab.fields.length > 0), [tabs]);

  const [activeTabKey, setActiveTabKey] = useState(initialTabKey ?? availableTabs[0]?.key ?? '');
  const [form, setForm] = useState<SettingsValues>(values);

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

  useEffect(() => {
    if (!availableTabs.find((tab) => tab.key === activeTabKey)) {
      setActiveTabKey(availableTabs[0]?.key ?? '');
    }
  }, [availableTabs, activeTabKey]);

  const activeTab = availableTabs.find((tab) => tab.key === activeTabKey) ?? availableTabs[0];

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

      onChange(next);

      return next;
    });
  };

  const renderField = (field: SettingsField) => {
    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);
        }}
      />
    );
  };

  return (
    <div className={styles.wrapper}>
      {availableTabs.length > 1 && (
        <div className={styles.tabs}>
          {availableTabs.map((tab) => (
            <button
              key={tab.key}
              type="button"
              className={tab.key === activeTabKey ? styles.tabActive : styles.tab}
              onClick={() => setActiveTabKey(tab.key)}
            >
              {tab.label}
            </button>
          ))}
        </div>
      )}

      <div className={styles.form}>{activeTab?.fields.map(renderField)}</div>
    </div>
  );
};



.wrapper {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.tabs {
  display: flex;
  gap: 16px;
  border-bottom: 1px solid var(--stroke-secondary);
}

.tab,
.tabActive {
  padding: 0 0 8px;
  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
  font: inherit;
}

.tabActive {
  border-bottom: 2px solid var(--text-accent);
}

.form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.checkboxField {
  display: flex;
  align-items: center;
  gap: 8px;
}