Загрузка данных
import { Checkbox } from 'exchange-elements/v2';
import styles from './index.module.scss';
interface CheckboxFieldProps {
label: string;
checked: boolean;
onValueChange: (value: boolean) => void;
}
export const CheckboxField = ({ label, checked, onValueChange }: CheckboxFieldProps) => {
return (
<label className={styles.checkboxField}>
<Checkbox
checked={checked}
onChange={(event) => {
onValueChange(Boolean(event.checked));
}}
/>
<span>{label}</span>
</label>
);
};
export type SettingValue = string | number | boolean;
export type SettingsValues = Record<string, SettingValue>;
interface BaseSettingField {
key: string;
label: string;
}
export interface NumberSettingField extends BaseSettingField {
type: 'number';
defaultValue: number;
min?: number;
max?: number;
}
export interface SelectSettingField extends BaseSettingField {
type: 'select';
defaultValue: string | number;
options: { label: string; value: string | number }[];
}
export interface ColorSettingField extends BaseSettingField {
type: 'color';
defaultValue: string;
}
export interface TextSettingField extends BaseSettingField {
type: 'text';
defaultValue: string;
placeholder?: string;
}
export interface TextAreaSettingField extends BaseSettingField {
type: 'textarea';
defaultValue: string;
placeholder?: string;
}
export interface BooleanSettingField extends BaseSettingField {
type: 'boolean';
defaultValue: boolean;
}
export type SettingField =
| NumberSettingField
| SelectSettingField
| ColorSettingField
| TextSettingField
| TextAreaSettingField
| BooleanSettingField;
export interface SettingsTab<TField extends SettingField = SettingField> {
key: string;
label: string;
fields: TField[];
}
import { useEffect, useMemo, useState } from 'react';
import {
CheckboxField,
ColorField,
NumberField,
SelectField,
TextAreaField,
TextField,
} from '@src/components/FormFields';
import { SettingField, SettingsTab, SettingsValues, SettingValue } from '@src/types/settings';
import styles from './index.module.scss';
interface EntitySettingsModalProps<
TValues extends SettingsValues,
TField extends SettingField = SettingField,
> {
tabs: SettingsTab<TField>[];
values: TValues;
onChange: (settings: TValues) => void;
initialTabKey?: string;
}
export const EntitySettingsModal = <
TValues extends SettingsValues,
TField extends SettingField = SettingField,
>({
tabs,
values,
onChange,
initialTabKey,
}: EntitySettingsModalProps<TValues, TField>) => {
const availableTabs = useMemo(() => tabs.filter((tab) => tab.fields.length > 0), [tabs]);
const [activeTabKey, setActiveTabKey] = useState(initialTabKey ?? availableTabs[0]?.key ?? '');
const [form, setForm] = useState<TValues>(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: SettingValue) => {
setForm((current) => {
const next = {
...current,
[key]: value,
} as TValues;
onChange(next);
return next;
});
};
const renderField = (field: TField) => {
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 (
<CheckboxField
key={field.key}
label={field.label}
checked={Boolean(value)}
onValueChange={(nextValue) => {
changeValue(field.key, nextValue);
}}
/>
);
}
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>
);
};