Загрузка данных
import { IChartApi, ISeriesApi, SeriesType } from 'lightweight-charts';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { EventManager } from '@core';
import { DOMModel } from '@core/DOMModel';
import { Drawing } from '@core/Drawings';
import { ISeriesDrawing } from '@core/Drawings/common';
import { Ray } from '@core/Drawings/ray';
import { TrendLine } from '@core/Drawings/trendLine';
import { VolumeProfile } from '@core/Drawings/volumeProfile';
import { AxisLine } from '@src/core/Drawings/axisLine';
import { Diapson } from '@src/core/Drawings/diapson';
import { Rectangle } from '@src/core/Drawings/rectangle';
import { Ruler } from '@src/core/Drawings/ruler';
import { SliderPosition } from '@src/core/Drawings/sliderPosition';
import { Traectory } from '@src/core/Drawings/traectory';
import { SeriesStrategies } from '@src/modules/series-strategies/SeriesFactory';
import { ChartTypeOptions } from '@src/types';
interface DrawingsManagerParams {
eventManager: EventManager;
mainSeries$: Observable<SeriesStrategies | null>;
lwcChart: IChartApi;
chartOptions?: ChartTypeOptions;
DOM: DOMModel;
container: HTMLElement;
}
export enum DrawingsNames {
'trendLine' = 'trendLine',
'ray' = 'ray',
'horizontalLine' = 'horizontalLine',
'horizontalRay' = 'horizontalRay',
'verticalLine' = 'verticalLine',
'ruler' = 'ruler',
'sliderLong' = 'sliderLong',
'sliderShort' = 'sliderShort',
'diapsonDates' = 'diapsonDates',
'diapsonPrices' = 'diapsonPrices',
'fixedProfile' = 'fixedProfile',
'rectangle' = 'rectangle',
'traectory' = 'traectory',
}
export interface DrawingParams {
chart: IChartApi;
series: ISeriesApi<SeriesType>;
eventManager: EventManager;
container: HTMLElement;
removeSelf: () => void;
}
export interface DrawingConfig {
singleInstance?: boolean;
construct: (params: DrawingParams) => ISeriesDrawing;
}
export const drawingsMap: Record<DrawingsNames, DrawingConfig> = {
[DrawingsNames.trendLine]: {
construct: ({ chart, series, container, eventManager, removeSelf }) => {
return new TrendLine(chart, series, {
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.ray]: {
construct: ({ chart, series, container, eventManager, removeSelf }) => {
return new Ray(chart, series, {
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.horizontalLine]: {
construct: ({ chart, series, eventManager, container, removeSelf }) => {
return new AxisLine(chart, series, {
direction: 'horizontal',
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.horizontalRay]: {
construct: ({ chart, series, container, eventManager, removeSelf }) => {
return new TrendLine(chart, series, {
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.verticalLine]: {
construct: ({ chart, series, eventManager, container, removeSelf }) => {
return new AxisLine(chart, series, {
direction: 'vertical',
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.sliderLong]: {
construct: ({ chart, series, eventManager, container, removeSelf }) => {
return new SliderPosition(chart, series, {
side: 'long',
container,
formatObservable: eventManager.getChartOptionsModel(),
resetTriggers: [eventManager.getTimeframeObs(), eventManager.getInterval()],
removeSelf,
});
},
},
[DrawingsNames.sliderShort]: {
construct: ({ chart, series, eventManager, container, removeSelf }) => {
return new SliderPosition(chart, series, {
side: 'short',
container,
formatObservable: eventManager.getChartOptionsModel(),
resetTriggers: [eventManager.getTimeframeObs(), eventManager.getInterval()],
removeSelf,
});
},
},
[DrawingsNames.diapsonDates]: {
construct: ({ chart, series, container, eventManager, removeSelf }) => {
return new Diapson(chart, series, {
rangeMode: 'date',
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.diapsonPrices]: {
construct: ({ chart, series, container, eventManager, removeSelf }) => {
return new Diapson(chart, series, {
rangeMode: 'price',
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.fixedProfile]: {
construct: ({ chart, series, container, eventManager, removeSelf }) => {
return new VolumeProfile(chart, series, {
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.rectangle]: {
construct: ({ chart, series, eventManager, container, removeSelf }) => {
return new Rectangle(chart, series, {
container,
formatObservable: eventManager.getChartOptionsModel(),
removeSelf,
});
},
},
[DrawingsNames.ruler]: {
singleInstance: true,
construct: ({ chart, series, eventManager, removeSelf }) => {
return new Ruler(chart, series, {
formatObservable: eventManager.getChartOptionsModel(),
resetTriggers: [eventManager.getTimeframeObs(), eventManager.getInterval()],
removeSelf,
});
},
},
[DrawingsNames.traectory]: {
construct: ({ chart, series, eventManager, removeSelf, container }) => {
return new Traectory(chart, series, {
formatObservable: eventManager.getChartOptionsModel(),
container,
removeSelf,
});
},
},
};
export class DrawingsManager {
private eventManager: EventManager;
private lwcChart: IChartApi;
private chartOptions?: ChartTypeOptions;
private drawingsQty = 0; // todo: replace with hash
private DOM: DOMModel;
private container: HTMLElement;
private mainSeries: SeriesStrategies | null = null;
private subscriptions = new Subscription();
private drawings$: BehaviorSubject<Drawing[]> = new BehaviorSubject<Drawing[]>([]);
constructor({ eventManager, mainSeries$, lwcChart, chartOptions, DOM, container }: DrawingsManagerParams) {
this.DOM = DOM;
this.eventManager = eventManager;
this.lwcChart = lwcChart;
this.chartOptions = chartOptions;
this.container = container;
this.subscriptions.add(
mainSeries$.subscribe((s) => {
if (!s) return;
this.mainSeries = s;
this.drawings$.value.forEach((drawing) => drawing.rebind(s));
}),
);
}
public addDrawing(name: DrawingsNames) {
if (!this.mainSeries) {
throw new Error('[Drawings] main series is not defined');
}
this.removePendingDrawings();
const config = drawingsMap[name];
if (config.singleInstance) {
this.removeDrawingsByName(name);
}
const drawingId = `${name}${this.drawingsQty}`;
const construct = (chart: IChartApi, series: ISeriesApi<SeriesType>) =>
config.construct({
chart,
series,
eventManager: this.eventManager,
container: this.container,
removeSelf: () => this.removeDrawing(drawingId),
});
const drawing = (zIndex: number, moveUp: (id: string) => void, moveDown: (id: string) => void) =>
new Drawing({
lwcChart: this.lwcChart,
mainSeries: this.mainSeries as SeriesStrategies,
id: drawingId,
onDelete: this.removeDrawing,
zIndex,
moveDown,
moveUp,
construct,
});
const trendLine = this.DOM.setEntity<Drawing>(drawing);
this.drawingsQty++;
this.drawings$.next([...this.drawings$.value, trendLine]);
}
private removeDrawing = (id: string) => {
const drawings = this.drawings$.value;
const objectToRemove = drawings.find((d) => d.id === id);
if (!objectToRemove) {
return;
}
this.removeDrawings([objectToRemove]);
};
private removeDrawingsByName(name: DrawingsNames): void {
const drawingsToRemove = this.drawings$.value.filter((drawing) => drawing.id.startsWith(name));
this.removeDrawings(drawingsToRemove);
}
private removePendingDrawings(): void {
const drawingsToRemove = this.drawings$.value.filter((drawing) => drawing.isCreationPending());
this.removeDrawings(drawingsToRemove);
}
private removeDrawings(drawingsToRemove: Drawing[]): void {
if (!drawingsToRemove.length) {
return;
}
drawingsToRemove.forEach((drawing) => {
drawing.destroy();
this.DOM.removeEntity(drawing);
});
this.drawings$.next(this.drawings$.value.filter((drawing) => !drawingsToRemove.includes(drawing)));
}
public entities(): Observable<Drawing[]> {
return this.drawings$.asObservable();
}
public getDrawings() {}
public hideAll() {}
}