Загрузка данных
import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { CompareManager } from '@core/CompareManager';
import { DateFormat, IMoexChart, MoexChart, Timeframes } from '@lib';
import { IndicatorsIds } from '@lib/constants';
import { CompareMode } from '@lib/types';
// import { argTypes } from '../argTypes';
import { dataSourceProvider } from '../common';
import type { Meta, StoryObj } from '@storybook/react';
import 'moex-chart/dist/styles.css';
/**
* ## TradeRadar story
* describes an entry point of MoexChart for TR user
*/
type TRProps = Omit<IMoexChart, 'container'>;
const TREntry = (props: TRProps) => {
const [isCompareOpen, setIsCompareOpen] = useState(false);
const [moexChart, setMoexChart] = useState<MoexChart | undefined>(undefined);
const [snap, setSnap] = useState<any | undefined>(undefined);
const containerRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const container = containerRef.current;
if (!container) {
return;
}
const chart = new MoexChart({
...props,
container,
chartCollectionPreset: {
...props.chartCollectionPreset,
openCompareModal: () => setIsCompareOpen(true),
},
});
setMoexChart(chart);
return () => {
chart.destroy();
};
}, [props]);
return (
<div style={{ padding: '20px' }}>
<h3>TradeRadar usage</h3>
<button
type="button"
onClick={() => {
setSnap(moexChart?.getSnapshot());
}}
>
Сохранить стейт
</button>
<button
type="button"
onClick={() => {
if (snap) {
moexChart?.setSnapshot(snap);
}
}}
>
Применить стейт
</button>
<div style={{ height: 'calc(100vh - 88px)', width: '100%' }}>
<div ref={containerRef} />
{isCompareOpen &&
createPortal(
<Modal
onClose={() => setIsCompareOpen(false)}
compareManager={moexChart?.getCompareManager() ?? null}
/>,
document.body,
)}
</div>
</div>
);
};
const meta: Meta<TRProps> = {
title: 'TradeRadar',
component: TREntry,
// argTypes, // todo: пофиксить вместе с переписыванием доки
parameters: {
layout: 'fullscreen',
docs: {
description: {
component: `## TradeRadar story
describes an entry point of MoexChart for TR user`,
},
},
},
};
export default meta;
type Story = StoryObj<typeof meta>;
const args: TRProps = {
snapshot: {
charts: [
{
timeframe: Timeframes['10s'],
chartSeriesType: 'Candlestick',
symbol: 'APPL',
panes: [
{
// empty panes deletes automatically
isMain: true, // Be careful. There is only one main pane can be present
id: 0,
indicators: [
{
indicatorType: IndicatorsIds.Volume,
},
],
drawings: [],
},
{
isMain: false,
id: 1,
indicators: [
{
indicatorType: IndicatorsIds.MACD,
},
],
drawings: [],
},
],
},
],
},
chartCollectionPreset: {
undoRedoEnabled: true,
showMenuButton: true,
showBottomPanel: true,
showControlBar: true,
showFullscreenButton: true,
showSettingsButton: true,
showCompareButton: true,
tooltipConfig: {
showTooltip: false,
time: { visible: true, label: 'Время' },
close: { visible: true, label: 'Закр.' },
change: { visible: true, label: 'Изм.' },
volume: { visible: true, label: 'Объем' },
open: { visible: true, label: 'Откр.' },
high: { visible: true, label: 'Макс.' },
low: { visible: true, label: 'Мин.' },
},
supportedTimeframes: [
Timeframes['1s'],
Timeframes['5s'],
Timeframes['10s'],
Timeframes['1m'],
Timeframes['2m'],
Timeframes['30m'],
Timeframes['1h'],
Timeframes['2h'],
Timeframes['1d'],
Timeframes['1w'],
],
supportedChartSeriesTypes: ['Candlestick', 'Line', 'Bar'],
getDataSource: dataSourceProvider.generateCandles.bind(dataSourceProvider),
startRealtime: (getSymbols, getTimeframe, update) =>
dataSourceProvider.startRealtime(getSymbols, getTimeframe, update), // should return unsub
theme: 'tr',
ohlc: {
show: true,
precision: 4,
},
mode: 'dark',
},
lwcInheritedChartOptions: {
timeVisible: true,
secondsVisible: false,
timeFormat: '24h',
dateFormat: DateFormat.DD_MM_YYYY_HH_mm_ss,
},
};
export const TradeRadar: Story = {
args,
parameters: {
controls: {
expanded: true, // отвечает за расширение колонок(+Description, +Default) в табе controls
},
},
};
const Modal = ({ onClose, compareManager }: { onClose: () => void; compareManager: CompareManager | null }) => {
const [isNewScaleDisabled, setIsNewScaleDisabled] = useState(false);
useEffect(() => {
if (!compareManager) {
setIsNewScaleDisabled(false);
return;
}
setIsNewScaleDisabled(compareManager.isNewScaleDisabled());
const subscription = compareManager.isNewScaleDisabledObservable().subscribe(setIsNewScaleDisabled);
return () => subscription.unsubscribe();
}, [compareManager]);
return (
<div
onClick={onClose}
style={{
width: '100%',
height: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#0000004D',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
zIndex: 999,
cursor: 'pointer',
}}
>
<div
onClick={(e) => e.stopPropagation()}
style={{
display: 'grid',
gap: 16,
padding: 16,
backgroundColor: 'white',
}}
>
{['SBER', 'APAX', 'SOL'].map((symbol) => (
<div
key={symbol}
style={{ display: 'flex', justifyContent: 'space-between', gap: 16 }}
>
<span>{symbol}</span>
<div style={{ display: 'flex', gap: 8 }}>
<button
onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.Percentage)}
style={{ backgroundColor: 'lightgray', padding: '2px 8px' }}
type="button"
>
%
</button>
<button
onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.NewScale)}
style={{
backgroundColor: isNewScaleDisabled ? 'darkgray' : 'lightgray',
padding: '2px 8px',
cursor: isNewScaleDisabled ? 'not-allowed' : 'cursor',
}}
disabled={isNewScaleDisabled}
type="button"
>
Новая шкала
</button>
<button
onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.NewPane)}
style={{ backgroundColor: 'lightgray', padding: '2px 8px' }}
type="button"
>
Новая панель
</button>
</div>
</div>
))}
</div>
</div>
);
};