//@ts-nocheck import { Annotations, PlotMouseEvent, PlotlyHTMLElement, } from "plotly.js-dist-min"; import { Figure } from "react-plotly.js"; type PopupData = { x: number; y: number; yref: string; text: string; yshift: number; yanchor: string; bordercolor: string; color: string; size: number; bgcolor?: string; arrowcolor?: string; arrowsize?: number; arrowwidth?: number; annotation?: any; high?: number; low?: number; }; export function add_annotation({ plotData, popup_data, current_text, }: { plotData: Figure; popup_data: PopupData; current_text?: string; }) { const x = popup_data.x; let y = popup_data.y; const yref = popup_data.yref; const annotations = plotData?.layout?.annotations || []; let index = -1; for (let i = 0; i < annotations.length; i++) { if ( annotations[i].x === x && annotations[i].y === y && annotations[i].text === current_text ) { index = i; break; } } if (popup_data.high !== undefined) { y = popup_data.yanchor === "above" ? popup_data.high : popup_data.low; } if (index === -1) { const annotation: Annotations = { x: x, y: y, xref: "x", yref: yref, xanchor: "center", text: popup_data.text, showarrow: true, arrowhead: 2, arrowsize: popup_data.arrowsize || 1, arrowwidth: popup_data.arrowwidth || 2, ax: x, ay: y + popup_data.yshift, ayref: yref, axref: "x", bordercolor: popup_data.bordercolor, bgcolor: popup_data.bgcolor || "#000000", arrowcolor: popup_data.arrowcolor || popup_data.bordercolor, borderwidth: 2, borderpad: 4, opacity: 0.8, font: { color: popup_data.color, size: popup_data.size, }, clicktoshow: "onoff", captureevents: true, high: popup_data.high || undefined, low: popup_data.low || undefined, }; annotations.push(annotation); } else { annotations[index].y = y; annotations[index].text = popup_data.text; annotations[index].font.color = popup_data.color; annotations[index].font.size = popup_data.size; annotations[index].ay = y + popup_data.yshift; annotations[index].bordercolor = popup_data.bordercolor; annotations[index].bgcolor = popup_data.bgcolor || "#000000"; annotations[index].arrowcolor = popup_data.arrowcolor || popup_data.bordercolor; annotations[index].arrowsize = popup_data.arrowsize || 1; annotations[index].arrowwidth = popup_data.arrowwidth || 2; annotations[index].high = popup_data.high || undefined; annotations[index].low = popup_data.low || undefined; } return { annotations: annotations, annotation: annotations[index] }; } export function plot_text({ plotData, popup_data, current_text, }: { plotData: Figure; popup_data: PopupData; current_text?: string; }) { // Plots text on the chart based on the popup_data // If current_text is not null, it will be replaced with the new text // If current_text is null, a new annotation will be added // popup_data is the data from the popup // data is the data from the chart console.log("plot_text: current_text", current_text); let output = undefined; const yaxis = popup_data.yref.replace("y", "yaxis"); const yrange = plotData.layout[yaxis].range; let yshift = (yrange[1] - yrange[0]) * 0.2; if (popup_data.yanchor === "below") { yshift = -yshift; } popup_data.yshift = yshift; output = add_annotation({ plotData, popup_data, current_text }); const to_update = { annotations: output.annotations, dragmode: "pan" }; to_update[`${yaxis}.type`] = "linear"; return { update: to_update, annotation: output.annotation }; } export function init_annotation({ plotData, popupData, setPlotData, setModal, setOnAnnotationClick, setAnnotations, onAnnotationClick, ohlcAnnotation, setOhlcAnnotation, annotations, plotDiv, }: { plotData: Figure; popupData: Partial; setPlotData: (plotData: Partial
) => void; setModal: (modal: { name: string; data?: any }) => void; onAnnotationClick: any; setOnAnnotationClick: (onAnnotationClick: any) => void; setAnnotations: (annotations: Partial[]) => void; ohlcAnnotation: any; setOhlcAnnotation: (ohlcAnnotation: any) => void; annotations: Annotations[]; plotDiv: PlotlyHTMLElement; }) { if (popupData.text !== undefined && popupData.text !== "") { popupData.text = popupData.text.replace(/\n/g, "
"); let popup_data: Partial; let inOhlc = false; if (popupData.annotation) { console.log("data", popupData); popup_data = { x: popupData.annotation.x, y: popupData.annotation.y, yref: popupData.annotation.yref, yanchor: popupData.annotation.y < popupData.annotation.ay ? "above" : "below", ...popupData, }; if (popupData.annotation.high !== undefined) { inOhlc = true; } console.log("popup_data", popup_data); const to_update = plot_text({ plotData, popup_data: popup_data as PopupData, current_text: popupData.annotation.text, }); if (inOhlc) { // we update the ohlcAnnotation const ohlcAnnotationIndex = ohlcAnnotation.findIndex( (a) => a.x === popupData.annotation.x && a.y === popupData.annotation.y && a.yref === popupData.annotation.yref, ); console.log("ohlcAnnotationIndex", ohlcAnnotationIndex); if (ohlcAnnotationIndex === -1) { // we add the annotation to the ohlcAnnotation array setOhlcAnnotation([...ohlcAnnotation, to_update.annotation]); } else { // we replace the annotation in the ohlcAnnotation array ohlcAnnotation[ohlcAnnotationIndex] = to_update.annotation; setOhlcAnnotation(ohlcAnnotation); } } setAnnotations( [...annotations, to_update.annotation].filter((a) => a !== undefined), ); plotData.layout.dragmode = "pan"; setPlotData({ ...plotData, ...to_update.update }); setOnAnnotationClick({}); return; } // First remove any existing click handlers to avoid duplicates plotDiv.removeAllListeners("plotly_clickannotation"); plotDiv.removeAllListeners("plotly_click"); // Add handler for clicking on existing annotations plotDiv.on("plotly_clickannotation", (eventData) => { console.log("plotly_clickannotation", eventData); const annotation = eventData.annotation; if (annotation.text === undefined) { console.log("annotation.text is undefined"); return; } console.log("annotation.text", annotation.text); // we replace
with \n so that the textarea can display the text properly annotation.text = annotation.text.replace(/
/g, "\n"); const popup_data = { x: annotation.x, y: annotation.y, high: annotation?.high ?? undefined, low: annotation?.low ?? undefined, yanchor: annotation.y < annotation.ay ? "above" : "below", text: annotation.text, color: annotation.font.color, size: annotation.font.size, bordercolor: annotation.bordercolor, annotation: annotation, }; console.log("popup_data_clickannotation", popup_data); setOnAnnotationClick(popup_data); setModal({ name: "textDialog", data: popup_data }); setOnAnnotationClick({}); }); // Add handler for adding a new annotation on click function clickHandler(eventData: PlotMouseEvent) { console.log("plotly_click", eventData); const x = eventData.points[0].x; const yaxis = eventData.points[0].fullData.yaxis; let y = 0; let high; let low; // We need to check if the trace is a candlestick or not // this is because the y value is stored in the high or low if (eventData.points[0].y !== undefined) { y = eventData.points[0].y; } else if (eventData.points[0].low !== undefined) { high = eventData.points[0].high; low = eventData.points[0].low; if (popup_data?.yanchor === "below") { y = eventData.points[0].low; } else { y = eventData.points[0].high; } } popup_data = { x: onAnnotationClick?.annotation?.x ?? x, y: onAnnotationClick?.annotation?.y ?? y, yref: onAnnotationClick?.annotation?.yref ?? yaxis, high: onAnnotationClick?.annotation?.high ?? high, low: onAnnotationClick?.annotation?.low ?? low, ...popupData, }; if (high !== undefined) { // save the annotation to use later ohlcAnnotation.push(popup_data); setOhlcAnnotation(ohlcAnnotation); console.log("ohlcAnnotation", ohlcAnnotation); } const to_update = plot_text({ plotData, popup_data: popup_data as PopupData, current_text: onAnnotationClick?.annotation?.text, }); setAnnotations( [...annotations, to_update.annotation].filter((a) => a !== undefined), ); // Important: update plotData with the new annotations to make them visible plotData.layout.dragmode = "pan"; setPlotData({ ...plotData, ...to_update.update }); // Force a relayout to ensure annotations appear Plotly.relayout(plotDiv, {'annotations': to_update.update.annotations, dragmode: "pan"}); // Remove click handler after creating the annotation plotDiv.removeAllListeners("plotly_click"); } // Set up dragmode and add the click handler plotData.layout.dragmode = "select"; setPlotData({ ...plotData }); // Ensure we add the click handler plotDiv.on("plotly_click", clickHandler); } }