CatPtain's picture
Upload 100 files
ccefd0b verified
//@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);
}
}