Download frontend-components/plotly/src/utils/addAnnotation.tsx from CatPtain/OpenBB: direct link, hf CLI and curl.
- Browser
- Download file 9.9 kB
-
https://huggingface.co/spaces/CatPtain/OpenBB/resolve/main/frontend-components/plotly/src/utils/addAnnotation.tsx
- Command line
-
hf download hf://spaces/CatPtain/OpenBB/frontend-components/plotly/src/utils/addAnnotation.tsx
-
curl -L -o addAnnotation.tsx https://huggingface.co/spaces/CatPtain/OpenBB/resolve/main/frontend-components/plotly/src/utils/addAnnotation.tsx
9.9 kB
| //@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<PopupData>; | |
| setPlotData: (plotData: Partial<Figure>) => void; | |
| setModal: (modal: { name: string; data?: any }) => void; | |
| onAnnotationClick: any; | |
| setOnAnnotationClick: (onAnnotationClick: any) => void; | |
| setAnnotations: (annotations: Partial<Annotations>[]) => void; | |
| ohlcAnnotation: any; | |
| setOhlcAnnotation: (ohlcAnnotation: any) => void; | |
| annotations: Annotations[]; | |
| plotDiv: PlotlyHTMLElement; | |
| }) { | |
| if (popupData.text !== undefined && popupData.text !== "") { | |
| popupData.text = popupData.text.replace(/\n/g, "<br>"); | |
| let popup_data: Partial<PopupData>; | |
| 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 <br> with \n so that the textarea can display the text properly | |
| annotation.text = annotation.text.replace(/<br>/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); | |
| } | |
| } | |