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; } }) } }