Загрузка данных
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<TField extends SettingsField = SettingsField> {
key: string;
label: string;
fields: TField[];
}
interface EntitySettingsModalProps<TValues extends SettingsValues, TField extends SettingsField = SettingsField> {
tabs: SettingsTab<TField>[];
values: TValues;
onChange: (settings: TValues) => void;
initialTabKey?: string;
}
export const EntitySettingsModal = <TValues extends SettingsValues, TField extends SettingsField = SettingsField>({
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: SettingsValue) => {
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);
}}
/>
);
}
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>
)}
{activeTab?.fields.map(renderField)}
</div>
);
};
import type { ISeriesApi, SeriesOptionsMap, Time } from 'lightweight-charts';
export type SeriesApi = ISeriesApi<keyof SeriesOptionsMap, Time>;
export interface Point {
x: number;
y: number;
}
export interface Bounds {
left: number;
right: number;
top: number;
bottom: number;
}
export interface ContainerSize {
width: number;
height: number;
}
export interface Anchor {
time: Time;
price: number;
}
export interface AxisSegment {
from: number;
to: number;
color: string;
}
export interface AxisLabel {
coordinate: number;
text: string;
textColor: string;
backgroundColor: string;
}
export interface UpdatableView {
update(): void;
}
export type DrawingStyleValue = string | number | boolean;
export type DrawingStyleSettings = Record<string, DrawingStyleValue>;
interface BaseDrawingStyleField {
key: string;
label: string;
}
export interface NumberDrawingStyleField extends BaseDrawingStyleField {
type: 'number';
defaultValue: number;
min?: number;
max?: number;
}
export interface SelectDrawingStyleField extends BaseDrawingStyleField {
type: 'select';
defaultValue: string | number;
options: { label: string; value: string | number }[];
}
export interface ColorDrawingStyleField extends BaseDrawingStyleField {
type: 'color';
defaultValue: string;
}
export interface TextDrawingStyleField extends BaseDrawingStyleField {
type: 'text';
defaultValue: string;
placeholder?: string;
}
export interface TextAreaDrawingStyleField extends BaseDrawingStyleField {
type: 'textarea';
defaultValue: string;
placeholder?: string;
}
export type DrawingStyleField =
| NumberDrawingStyleField
| SelectDrawingStyleField
| ColorDrawingStyleField
| TextDrawingStyleField
| TextAreaDrawingStyleField;
import {
DeepPartial,
HistogramData,
LineData,
LineWidth,
PriceScaleOptions,
SeriesDataItemTypeMap,
SeriesPartialOptionsMap,
SeriesType,
Time,
WhitespaceData,
} from 'lightweight-charts';
import { indicatorLabelById, IndicatorsIds } from '@src/constants';
import { IndicatorDataFormatter } from '@src/core/Indicators';
import { ChartSeriesType } from '@src/types/chart';
export type IndicatorData = HistogramData<Time> | LineData<Time> | WhitespaceData<Time>;
export type IndicatorType = 'SMA' | 'EMA' | 'RSI' | 'OHLC' | 'VOL';
export type MASource = 'open' | 'high' | 'low' | 'close';
export type IndicatorSetting = NumberIndicatorSetting | SelectIndicatorSetting;
export type IndicatorSettings = Record<string, string | number>;
export type IndicatorLabel = (typeof indicatorLabelById)[IndicatorsIds];
export interface IndicatorStateConfig {
type: IndicatorType;
id?: IndicatorsIds;
period?: number;
color?: string;
lineWidth?: LineWidth;
visible?: boolean;
params?: Record<string, any>;
pane?: string;
}
interface BaseIndicatorSetting {
key: string;
label: string;
}
interface NumberIndicatorSetting extends BaseIndicatorSetting {
type: 'number';
defaultValue: number;
min?: number;
max?: number;
}
interface SelectIndicatorSetting extends BaseIndicatorSetting {
type: 'select';
defaultValue: string;
options: { label: string; value: string | number }[];
}
export interface IndicatorSerie {
id: string;
name: ChartSeriesType;
seriesOptions?: SeriesPartialOptionsMap[ChartSeriesType];
priceScaleOptions?: DeepPartial<PriceScaleOptions>;
priceScaleId?: string;
dataFormatter?<T extends SeriesType>(params: IndicatorDataFormatter<T>): SeriesDataItemTypeMap<Time>[T][];
}
export interface IndicatorConfig {
series: IndicatorSerie[];
settings?: IndicatorSetting[];
newPane?: boolean;
label?: string;
seriesLabels?: Record<string, string>;
}