import { DrawingTool } from "../drawing/drawing-tool";
import { TrendLine } from "../trend-line/trend-line";
import { Box } from "../box/box";
import { Drawing } from "../drawing/drawing";
import { ContextMenu } from "../context-menu/context-menu";
import { GlobalParams } from "./global-params";
import { IChartApi, ISeriesApi, SeriesType } from "lightweight-charts";
import { HorizontalLine } from "../horizontal-line/horizontal-line";
import { RayLine } from "../horizontal-line/ray-line";
import { VerticalLine } from "../vertical-line/vertical-line";
interface Icon {
div: HTMLDivElement,
group: SVGGElement,
type: new (...args: any[]) => Drawing
}
declare const window: GlobalParams
export class ToolBox {
private static readonly TREND_SVG: string = '';
private static readonly HORZ_SVG: string = '';
private static readonly RAY_SVG: string = '';
private static readonly BOX_SVG: string = '';
private static readonly VERT_SVG: string = ToolBox.RAY_SVG;
div: HTMLDivElement;
private activeIcon: Icon | null = null;
private buttons: HTMLDivElement[] = [];
private _commandFunctions: Function[];
private _handlerID: string;
private _drawingTool: DrawingTool;
constructor(handlerID: string, chart: IChartApi, series: ISeriesApi, commandFunctions: Function[]) {
this._handlerID = handlerID;
this._commandFunctions = commandFunctions;
this._drawingTool = new DrawingTool(chart, series, () => this.removeActiveAndSave());
this.div = this._makeToolBox()
new ContextMenu(this.saveDrawings, this._drawingTool);
commandFunctions.push((event: KeyboardEvent) => {
if ((event.metaKey || event.ctrlKey) && event.code === 'KeyZ') {
const drawingToDelete = this._drawingTool.drawings.pop();
if (drawingToDelete) this._drawingTool.delete(drawingToDelete)
return true;
}
return false;
});
}
toJSON() {
// Exclude the chart attribute from serialization
const { ...serialized} = this;
return serialized;
}
private _makeToolBox() {
let div = document.createElement('div')
div.classList.add('toolbox');
this.buttons.push(this._makeToolBoxElement(TrendLine, 'KeyT', ToolBox.TREND_SVG))
this.buttons.push(this._makeToolBoxElement(HorizontalLine, 'KeyH', ToolBox.HORZ_SVG));
this.buttons.push(this._makeToolBoxElement(RayLine, 'KeyR', ToolBox.RAY_SVG));
this.buttons.push(this._makeToolBoxElement(Box, 'KeyB', ToolBox.BOX_SVG));
this.buttons.push(this._makeToolBoxElement(VerticalLine, 'KeyV', ToolBox.VERT_SVG, true));
for (const button of this.buttons) {
div.appendChild(button);
}
return div
}
private _makeToolBoxElement(DrawingType: new (...args: any[]) => Drawing, keyCmd: string, paths: string, rotate=false) {
const elem = document.createElement('div')
elem.classList.add("toolbox-button");
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", "29");
svg.setAttribute("height", "29");
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
group.innerHTML = paths
group.setAttribute("fill", window.pane.color)
svg.appendChild(group)
elem.appendChild(svg);
const icon: Icon = {div: elem, group: group, type: DrawingType}
elem.addEventListener('click', () => this._onIconClick(icon));
this._commandFunctions.push((event: KeyboardEvent) => {
if (this._handlerID !== window.handlerInFocus) return false;
if (event.altKey && event.code === keyCmd) {
event.preventDefault()
this._onIconClick(icon);
return true
}
return false;
})
if (rotate == true) {
svg.style.transform = 'rotate(90deg)';
svg.style.transformBox = 'fill-box';
svg.style.transformOrigin = 'center';
}
return elem
}
private _onIconClick(icon: Icon) {
if (this.activeIcon) {
this.activeIcon.div.classList.remove('active-toolbox-button');
window.setCursor('crosshair');
this._drawingTool?.stopDrawing()
if (this.activeIcon === icon) {
this.activeIcon = null
return
}
}
this.activeIcon = icon
this.activeIcon.div.classList.add('active-toolbox-button')
window.setCursor('crosshair');
this._drawingTool?.beginDrawing(this.activeIcon.type);
}
removeActiveAndSave = () => {
window.setCursor('default');
if (this.activeIcon) this.activeIcon.div.classList.remove('active-toolbox-button')
this.activeIcon = null
this.saveDrawings()
}
addNewDrawing(d: Drawing) {
this._drawingTool.addNewDrawing(d);
}
clearDrawings() {
this._drawingTool.clearDrawings();
}
saveDrawings = () => {
const drawingMeta = []
for (const d of this._drawingTool.drawings) {
drawingMeta.push({
type: d._type,
points: d.points,
options: d._options
});
}
const string = JSON.stringify(drawingMeta);
window.callbackFunction(`save_drawings${this._handlerID}_~_${string}`)
}
loadDrawings(drawings: any[]) { // TODO any
drawings.forEach((d) => {
switch (d.type) {
case "Box":
this._drawingTool.addNewDrawing(new Box(d.points[0], d.points[1], d.options));
break;
case "TrendLine":
this._drawingTool.addNewDrawing(new TrendLine(d.points[0], d.points[1], d.options));
break;
case "HorizontalLine":
this._drawingTool.addNewDrawing(new HorizontalLine(d.points[0], d.options));
break;
case "RayLine":
this._drawingTool.addNewDrawing(new RayLine(d.points[0], d.options));
break;
case "VerticalLine":
this._drawingTool.addNewDrawing(new VerticalLine(d.points[0], d.options));
break;
}
})
}
}