Загрузка данных
import type { PriceScaleMode } from 'lightweight-charts';
import type { ChartViewType } from './chartView';
export type SeasonalityScale = PriceScaleMode.Normal | PriceScaleMode.Percentage;
export interface SeasonalityRange {
fromYear: number;
toYear: number;
}
export interface SeasonalityState extends SeasonalityRange {
view: ChartViewType;
status: 'idle' | 'loading' | 'ready' | 'empty' | 'error';
scale: SeasonalityScale;
symbolId: string;
minYear: number;
maxYear: number;
error: string | null;
}
export interface SeasonalityPoint {
date: string;
chartDate: string;
price: number;
percentage: number | null;
}
export interface SeasonalityYear {
year: number;
baselineDate: string;
baselinePrice: number;
points: SeasonalityPoint[];
}
export interface DailyClose {
date: string;
close: number;
}
export enum ChartViewType {
Chart = 'chart',
Seasonality = 'seasonality',
}
import { PriceScaleMode } from 'lightweight-charts';
import { ChartViewType } from '@src/types/chartView';
import type { SeasonalityState } from '@src/types/seasonality';
export const DEFAULT_SEASONALITY_YEARS = 5;
export function createInitialSeasonalityState(): SeasonalityState {
const year = new Date().getUTCFullYear();
const fromYear = year - DEFAULT_SEASONALITY_YEARS + 1;
return {
view: ChartViewType.Chart,
status: 'idle',
scale: PriceScaleMode.Normal,
symbolId: '',
error: null,
minYear: fromYear,
maxYear: year,
fromYear,
toYear: year,
};
}
import { MenuList, type MenuOption } from '@src/components/Menu/MenuList';
import { t } from '@src/translations';
import { ChartViewType } from '@src/types/chartView';
interface ChartViewMenuProps {
selectedView: ChartViewType;
onClick: (view: ChartViewType) => void;
}
export function ChartViewMenu({ selectedView, onClick }: ChartViewMenuProps) {
const options: MenuOption<ChartViewType>[] = [
{
value: ChartViewType.Chart,
label: t('Chart'),
},
{
value: ChartViewType.Seasonality,
label: t('Seasonality'),
},
];
return (
<MenuList
mode="single"
value={selectedView}
options={options}
onClick={onClick}
/>
);
}
import { PriceScaleMode } from 'lightweight-charts';
import { MenuList, type MenuOption } from '@src/components/Menu/MenuList';
import { t } from '@src/translations';
import type { SeasonalityScale } from '@src/types/seasonality';
interface SeasonalityScaleMenuProps {
selectedScale: SeasonalityScale;
onClick: (scale: SeasonalityScale) => void;
}
export function SeasonalityScaleMenu({ selectedScale, onClick }: SeasonalityScaleMenuProps) {
const options: MenuOption<SeasonalityScale>[] = [
{
value: PriceScaleMode.Normal,
label: t('Price'),
},
{
value: PriceScaleMode.Percentage,
label: t('Percent'),
},
];
return (
<MenuList
mode="single"
value={selectedScale}
options={options}
onClick={onClick}
/>
);
}
import { PriceScaleMode } from 'lightweight-charts';
import { Dropdown } from '@src/components/Dropdown';
import { RangeField } from '@src/components/FormFields';
import { SeasonalityScaleMenu } from '@src/components/Menu/SeasonalityScaleMenu';
import { t } from '@src/translations';
import { ChartViewType } from '@src/types/chartView';
import { useObservable } from '@src/utils';
import styles from './index.module.scss';
import type { SeasonalityRange, SeasonalityScale, SeasonalityState } from '@src/types/seasonality';
import type { Observable } from 'rxjs';
interface SeasonalityControlsProps {
stateObs: Observable<SeasonalityState>;
setScale: (scale: SeasonalityScale) => void;
setRange: (range: SeasonalityRange) => void;
}
export function SeasonalityControls({ stateObs, setScale, setRange }: SeasonalityControlsProps) {
const state = useObservable(stateObs);
if (!state || state.view !== ChartViewType.Seasonality) {
return null;
}
const selectedScaleLabel = state.scale === PriceScaleMode.Normal
? t('Price')
: t('Percent');
const isRangeDisabled = state.status === 'loading' || state.status === 'error';
const handleRangeChange = ([fromYear, toYear]: [number, number]): void => {
setRange({ fromYear, toYear });
};
return (
<div className={styles.controls}>
<div className={styles.controls_header}>
<Dropdown selectedValue={selectedScaleLabel}>
<SeasonalityScaleMenu
selectedScale={state.scale}
onClick={setScale}
/>
</Dropdown>
</div>
<div className={styles.controls_range}>
<RangeField
mode="range"
startLabel={t('From year')}
endLabel={t('To year')}
value={[state.fromYear, state.toYear]}
min={state.minYear}
max={state.maxYear}
step={1}
suffix=""
size="sm"
disabled={isRangeDisabled}
onRangeChange={handleRangeChange}
/>
</div>
</div>
);
}
private setChartView = (view: ChartViewType): void => {
if (view === ChartViewType.Chart) {
this.backToChart();
return;
}
void this.showSeasonality();
};
const isSeasonality = seasonalityState.view === ChartViewType.Seasonality;
setChartView?: (view: ChartViewType) => void;