summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorteh_coderer <me@tehcoderer.com>2023-06-05 13:03:08 -0400
committerGitHub <noreply@github.com>2023-06-05 17:03:08 +0000
commite040e9e6c4e52d8f84c94dd7d2bd59d8906adfcd (patch)
treead018acb1506b2b58644758c0c9e1ffbcd3a883b
parentb81aa22385261a4a9054208b9bcb01174afadc76 (diff)
fix wrong default for `yaxisFixedRange` (#5106)
-rw-r--r--frontend-components/plotly/src/components/Chart.tsx1264
-rw-r--r--openbb_terminal/core/plots/plotly.html24
2 files changed, 644 insertions, 644 deletions
diff --git a/frontend-components/plotly/src/components/Chart.tsx b/frontend-components/plotly/src/components/Chart.tsx
index 1f32536cc98..2773302b807 100644
--- a/frontend-components/plotly/src/components/Chart.tsx
+++ b/frontend-components/plotly/src/components/Chart.tsx
@@ -1,662 +1,662 @@
// @ts-nocheck
+import clsx from "clsx";
import * as Plotly from "plotly.js-dist-min";
+import { Icons as PlotlyIcons } from "plotly.js-dist-min";
import { usePostHog } from "posthog-js/react";
+import { useEffect, useState } from "react";
import createPlotlyComponent from "react-plotly.js/factory";
-import { useState, useEffect } from "react";
-import { ICONS, DARK_CHARTS_TEMPLATE, LIGHT_CHARTS_TEMPLATE } from "./Config";
-import OverlayChartDialog from "./Dialogs/OverlayChartDialog";
-import TitleChartDialog from "./Dialogs/TitleChartDialog";
-import TextChartDialog from "./Dialogs/TextChartDialog";
-import { Icons as PlotlyIcons } from "plotly.js-dist-min";
import { init_annotation } from "../utils/addAnnotation";
-import { PlotConfig, hideModebar } from "./PlotlyConfig";
-import autoScaling from "./AutoScaling";
import { non_blocking, saveImage } from "../utils/utils";
-import ResizeHandler from "./ResizeHandler";
+import autoScaling from "./AutoScaling";
import ChangeColor from "./ChangeColor";
-import clsx from "clsx";
-import DownloadFinishedDialog from "./Dialogs/DownloadFinishedDialog";
+import { DARK_CHARTS_TEMPLATE, ICONS, LIGHT_CHARTS_TEMPLATE } from "./Config";
import AlertDialog from "./Dialogs/AlertDialog";
+import DownloadFinishedDialog from "./Dialogs/DownloadFinishedDialog";
+import OverlayChartDialog from "./Dialogs/OverlayChartDialog";
+import TextChartDialog from "./Dialogs/TextChartDialog";
+import TitleChartDialog from "./Dialogs/TitleChartDialog";
+import { PlotConfig, hideModebar } from "./PlotlyConfig";
+import ResizeHandler from "./ResizeHandler";
const Plot = createPlotlyComponent(Plotly);
function CreateDataXrange(data: Plotly.PlotData[], xrange?: any) {
- if (!xrange) {
- xrange = [
- data[0]?.x[data[0].x.length - 1000],
- data[0]?.x[data[0].x.length - 1],
- ];
- }
- const new_data = [];
- data.forEach((trace) => {
- const new_trace = { ...trace };
- const data_keys = [
- "x",
- "y",
- "low",
- "high",
- "open",
- "close",
- "text",
- "customdata",
- ];
- const xaxis = trace.x ? trace.x : [];
- const chunks = [];
- for (let i = 0; i < xaxis.length; i++) {
- if (xaxis[i] >= xrange[0] && xaxis[i] <= xrange[1]) {
- chunks.push(i);
- }
- }
- data_keys.forEach((key) => {
- if (trace[key] && Array.isArray(trace[key])) {
- new_trace[key] = trace[key].filter((_, i) => chunks.includes(i));
- }
- });
- const color_keys = ["marker", "line"];
- color_keys.forEach((key) => {
- if (trace[key]?.color && Array.isArray(trace[key].color)) {
- new_trace[key] = { ...trace[key] };
- new_trace[key].color = trace[key].color.filter((_, i) =>
- chunks.includes(i),
- );
- }
- });
- new_data.push(new_trace);
- });
-
- if (new_data.length === 0) return data;
-
- return new_data;
+ if (!xrange) {
+ xrange = [
+ data[0]?.x[data[0].x.length - 1000],
+ data[0]?.x[data[0].x.length - 1],
+ ];
+ }
+ const new_data = [];
+ data.forEach((trace) => {
+ const new_trace = { ...trace };
+ const data_keys = [
+ "x",
+ "y",
+ "low",
+ "high",
+ "open",
+ "close",
+ "text",
+ "customdata",
+ ];
+ const xaxis = trace.x ? trace.x : [];
+ const chunks = [];
+ for (let i = 0; i < xaxis.length; i++) {
+ if (xaxis[i] >= xrange[0] && xaxis[i] <= xrange[1]) {
+ chunks.push(i);
+ }
+ }
+ data_keys.forEach((key) => {
+ if (trace[key] && Array.isArray(trace[key])) {
+ new_trace[key] = trace[key].filter((_, i) => chunks.includes(i));
+ }
+ });
+ const color_keys = ["marker", "line"];
+ color_keys.forEach((key) => {
+ if (trace[key]?.color && Array.isArray(trace[key].color)) {
+ new_trace[key] = { ...trace[key] };
+ new_trace[key].color = trace[key].color.filter((_, i) =>
+ chunks.includes(i),
+ );
+ }
+ });
+ new_data.push(new_trace);
+ });
+
+ if (new_data.length === 0) return data;
+
+ return new_data;
}
async function DynamicLoad({
- event,
- figure,
+ event,
+ figure,
}: {
- event?: any;
- figure: any;
+ event?: any;
+ figure: any;
}) {
- try {
- const XDATA = figure.data.filter(
- (trace) =>
- trace.x !== undefined && trace.x.length > 0 && trace.x[0] !== undefined,
- );
-
- if (XDATA.length === 0) return figure;
- // We get the xaxis range, if no event is passed, we get the last 1000 points
- const xaxis_range = event
- ? [event["xaxis.range[0]"], event["xaxis.range[1]"]]
- : [
- XDATA[0]?.x[XDATA[0].x.length - 1000],
- XDATA[0]?.x[XDATA[0].x.length - 1],
- ];
-
- const new_data = CreateDataXrange(figure.data, xaxis_range);
- figure.data = new_data;
- figure.layout.xaxis.range = xaxis_range;
- return figure;
- } catch (e) {
- console.log("error", e);
- }
+ try {
+ const XDATA = figure.data.filter(
+ (trace) =>
+ trace.x !== undefined && trace.x.length > 0 && trace.x[0] !== undefined,
+ );
+
+ if (XDATA.length === 0) return figure;
+ // We get the xaxis range, if no event is passed, we get the last 1000 points
+ const xaxis_range = event
+ ? [event["xaxis.range[0]"], event["xaxis.range[1]"]]
+ : [
+ XDATA[0]?.x[XDATA[0].x.length - 1000],
+ XDATA[0]?.x[XDATA[0].x.length - 1],
+ ];
+
+ const new_data = CreateDataXrange(figure.data, xaxis_range);
+ figure.data = new_data;
+ figure.layout.xaxis.range = xaxis_range;
+ return figure;
+ } catch (e) {
+ console.log("error", e);
+ }
}
export default function Chart({
- json,
- date,
- cmd,
- title,
- globals,
- theme,
- info,
+ json,
+ date,
+ cmd,
+ title,
+ globals,
+ theme,
+ info,
}: {
- // @ts-ignore
- json: Figure;
- date: Date;
- cmd: string;
- title: string;
- globals: any;
- theme: string;
- info?: any;
+ // @ts-ignore
+ json: Figure;
+ date: Date;
+ cmd: string;
+ title: string;
+ globals: any;
+ theme: string;
+ info?: any;
}) {
- const posthog = usePostHog();
-
- useEffect(() => {
- if (posthog) posthog.capture("chart", info);
- }, []);
-
- json.layout.width = undefined;
- json.layout.height = undefined;
- if (json.layout?.title?.text) {
- json.layout.title.text = "";
- }
-
- const [originalData, setOriginalData] = useState(json);
- const [barButtons, setModeBarButtons] = useState({});
- const [LogYaxis, setLogYaxis] = useState(false);
- const [chartTitle, setChartTitle] = useState(title);
- const [axesTitles, setAxesTitles] = useState({});
- const [plotLoaded, setPlotLoaded] = useState(false);
- const [modal, setModal] = useState({ name: "" });
- const [loading, setLoading] = useState(false);
- const [plotDiv, setPlotDiv] = useState(null);
- const [volumeBars, setVolumeBars] = useState({ old_nticks: {} });
- const [maximizePlot, setMaximizePlot] = useState(false);
- const [downloadFinished, setDownloadFinished] = useState(false);
- const [dateSliced, setDateSliced] = useState(false);
-
- const [plotData, setPlotData] = useState(originalData);
- const [annotations, setAnnotations] = useState([]);
- const [changeTheme, setChangeTheme] = useState(false);
- const [darkMode, setDarkMode] = useState(true);
- const [autoScale, setAutoScaling] = useState(false);
- const [changeColor, setChangeColor] = useState(false);
- const [colorActive, setColorActive] = useState(false);
- const [onAnnotationClick, setOnAnnotationClick] = useState({});
- const [ohlcAnnotation, setOhlcAnnotation] = useState([]);
- const [yaxisFixedRange, setYaxisFixedRange] = useState({});
-
- const onClose = () => setModal({ name: "" });
-
- // @ts-ignore
- function onDeleteAnnotation(annotation) {
- console.log("onDeleteAnnotation", annotation);
- const index = plotData?.layout?.annotations?.findIndex(
- (a: any) => a.text === annotation.text,
- );
- console.log("index", index);
- if (index > -1) {
- plotData?.layout?.annotations?.splice(index, 1);
- setPlotData({ ...plotData });
- setAnnotations(plotData?.layout?.annotations);
- }
- }
-
- // @ts-ignore
- function onAddAnnotation(data) {
- init_annotation({
- plotData,
- popupData: data,
- setPlotData,
- setModal,
- setOnAnnotationClick,
- setAnnotations,
- onAnnotationClick,
- ohlcAnnotation,
- setOhlcAnnotation,
- annotations,
- plotDiv,
- });
- }
-
- useEffect(() => {
- if (downloadFinished) {
- setModal({ name: "downloadFinished" });
- setDownloadFinished(false);
- }
- }, [downloadFinished]);
-
- useEffect(() => {
- if (axesTitles && Object.keys(axesTitles).length > 0) {
- Object.keys(axesTitles).forEach((k) => {
- plotData.layout[k].title = {
- ...(plotData.layout[k].title || {}),
- text: axesTitles[k],
- };
- plotData.layout[k].showticklabels = true;
- });
- setAxesTitles({});
- }
- }, [axesTitles]);
-
- function onChangeColor(color) {
- // updates the color of the last added shape
- // this function is called when the color picker is used
- // if there are no shapes, we remove the color picker
- const shapes = plotDiv.layout.shapes;
- if (!shapes || shapes.length === 0) {
- return;
- }
- // we change last added shape color
- const last_shape = shapes[shapes.length - 1];
- last_shape.line.color = color;
- Plotly.update(plotDiv, {}, { shapes: shapes });
- }
-
- function button_pressed(title, active = false) {
- // changes the style of the button when it is pressed
- // title is the title of the button
- // active is true if the button is active, false otherwise
-
- const button =
- barButtons[title] || document.querySelector(`[data-title="${title}"]`);
- if (!active) {
- button.style.border = "1px solid rgba(0, 151, 222, 1.0)";
- button.style.borderRadius = "5px";
- button.style.borderpadding = "5px";
- button.style.boxShadow = "0 0 5px rgba(0, 151, 222, 1.0)";
- } else {
- button.style.border = "transparent";
- button.style.boxShadow = "none";
- }
- setModeBarButtons({ ...barButtons, [title]: button });
- }
-
- function autoscaleButton() {
- // We need to check if the button is active or not
- const title = "Auto Scale (Ctrl+Shift+A)";
- const button =
- barButtons[title] || document.querySelector(`[data-title="${title}"]`);
- let active = true;
-
- if (button.style.border === "transparent") {
- plotDiv.removeAllListeners("plotly_relayout");
- active = false;
- plotDiv.on(
- "plotly_relayout",
- non_blocking(async function (eventdata) {
- if (eventdata["xaxis.range[0]"] === undefined) return;
- if (dateSliced) {
- const data = { ...originalData };
- await DynamicLoad({
- event: eventdata,
- figure: data,
- }).then(async (to_update) => {
- setPlotData(to_update);
- Plotly.react(plotDiv, to_update.data, to_update.layout);
- const scaled = await autoScaling(eventdata, plotDiv);
- setYaxisFixedRange(scaled.yaxis_fixedrange);
- Plotly.update(plotDiv, {}, scaled.to_update);
- });
- } else {
- const scaled = await autoScaling(eventdata, plotDiv);
- setYaxisFixedRange(scaled.yaxis_fixedrange);
- Plotly.update(plotDiv, {}, scaled.to_update);
- }
- }, 100),
- );
- }
- // If the button isn't active, we remove the listener so
- // the graphs don't autoscale anymore
- else {
- plotDiv.removeAllListeners("plotly_relayout");
- yaxisFixedRange.forEach((yaxis) => {
- plotDiv.layout[yaxis].fixedrange = false;
- });
- setYaxisFixedRange({});
- if (dateSliced) {
- plotDiv.on(
- "plotly_relayout",
- non_blocking(async function (eventdata) {
- if (eventdata["xaxis.range[0]"] === undefined) return;
- const data = { ...originalData };
- await DynamicLoad({
- event: eventdata,
- figure: data,
- }).then(async (to_update) => {
- setPlotData(to_update);
- Plotly.react(plotDiv, to_update.data, to_update.layout);
- });
- }, 100),
- );
- }
- }
-
- button_pressed(title, active);
- }
-
- function changecolorButton() {
- // We need to check if the button is active or not
- const title = "Edit Color (Ctrl+E)";
- const button =
- barButtons[title] || document.querySelector(`[data-title="${title}"]`);
- let active = true;
-
- if (button.style.border === "transparent") {
- active = false;
- }
-
- setColorActive(!active);
- button_pressed(title, active);
- }
-
- useEffect(() => {
- if (autoScale) {
- const scale = !autoScale;
- console.log("activateAutoScale", scale);
- autoscaleButton();
- setAutoScaling(false);
- }
- }, [autoScale]);
-
- useEffect(() => {
- if (changeColor) {
- changecolorButton();
- setChangeColor(false);
- }
- }, [changeColor]);
-
- useEffect(() => {
- if (changeTheme) {
- try {
- console.log("changeTheme", changeTheme);
- const TRACES = plotData?.data.filter((trace) =>
- trace?.name?.startsWith("Volume"),
- );
- const darkmode = !darkMode;
-
- window.document.body.style.backgroundColor = darkmode ? "#000" : "#fff";
-
- plotData.layout.font = {
- ...(plotData.layout.font || {}),
- color: darkmode ? "#fff" : "#000",
- };
-
- const changeIcon = darkmode ? ICONS.sunIcon : ICONS.moonIcon;
-
- document
- .querySelector('[data-title="Change Theme"]')
- .getElementsByTagName("path")[0]
- .setAttribute("d", changeIcon.path);
-
- document
- .querySelector('[data-title="Change Theme"]')
- .getElementsByTagName("svg")[0]
- .setAttribute("viewBox", changeIcon.viewBox);
-
- const volumeColorsDark = {
- "#009600": "#00ACFF",
- "#c80000": "#e4003a",
- };
- const volumeColorsLight = {
- "#e4003a": "#c80000",
- "#00ACFF": "#009600",
- };
-
- const volumeColors = darkmode ? volumeColorsDark : volumeColorsLight;
-
- TRACES.forEach((trace) => {
- if (trace.type === "bar")
- trace.marker.color = trace.marker.color.map((color) => {
- return volumeColors[color] || color;
- });
- });
- plotData.layout.template = darkmode
- ? DARK_CHARTS_TEMPLATE
- : LIGHT_CHARTS_TEMPLATE;
- setPlotData({ ...plotData });
- Plotly.react(plotDiv, plotData.data, plotData.layout);
- setDarkMode(darkmode);
- setChangeTheme(false);
- } catch (e) {
- console.log("error", e);
- }
- }
- }, [changeTheme]);
-
- useEffect(() => {
- if (plotLoaded) {
- setDarkMode(true);
- setAutoScaling(false);
- const captureButtons = [
- "Download CSV",
- "Download Chart as Image",
- "Overlay chart from CSV",
- "Add Text",
- "Change Titles",
- "Auto Scale (Ctrl+Shift+A)",
- "Reset Axes",
- ];
- const autoscale = document.querySelector('[data-title="Autoscale"]');
- if (autoscale) {
- autoscale
- .getElementsByTagName("path")[0]
- .setAttribute("d", PlotlyIcons.home.path);
- autoscale.setAttribute("data-title", "Reset Axes");
- }
-
- window.MODEBAR = document.getElementsByClassName(
- "modebar-container",
- )[0] as HTMLElement;
- const modeBarButtons = window.MODEBAR.getElementsByClassName(
- "modebar-btn",
- ) as HTMLCollectionOf<HTMLElement>;
-
- window.MODEBAR.style.cssText = `${window.MODEBAR.style.cssText}; display:flex;`;
-
- if (modeBarButtons) {
- const barbuttons: any = {};
- for (let i = 0; i < modeBarButtons.length; i++) {
- const btn = modeBarButtons[i];
- if (captureButtons.includes(btn.getAttribute("data-title"))) {
- btn.classList.add("ph-capture");
- }
- btn.style.border = "transparent";
- barbuttons[btn.getAttribute("data-title")] = btn;
- }
- setModeBarButtons(barbuttons);
- }
-
- if (plotData?.layout?.yaxis?.type !== undefined) {
- if (plotData.layout.yaxis.type === "log" && !LogYaxis) {
- console.log("yaxis.type changed to log");
- setLogYaxis(true);
-
- // const layout_update = {
- // "yaxis.exponentformat": "none"
- // };
- // Plotly.update(plotDiv, {}, layout_update);
- }
- if (plotData.layout.yaxis.type === "linear" && LogYaxis) {
- console.log("yaxis.type changed to linear");
- setLogYaxis(false);
-
- // We update the yaxis exponent format to none,
- // set the tickformat to null and the exponentbase to 10
- const layout_update = {
- "yaxis.exponentformat": "none",
- "yaxis.tickformat": null,
- "yaxis.exponentbase": 10,
- };
- Plotly.update(plotDiv, {}, layout_update);
- }
- }
-
- // We check to see if window.export_image is defined
- if (window.export_image !== undefined) {
- // We get the extension of the file and check if it is valid
- const filename = window.export_image.split("/").pop();
- const extension = filename.split(".").pop().replace("jpg", "jpeg");
-
- if (["jpeg", "png", "svg", "pdf"].includes(extension))
- non_blocking(async function () {
- await hideModebar();
- await saveImage("MainChart", filename.split(".")[0], extension);
- }, 2)();
- }
-
- window.addEventListener("resize", async function () {
- const update = await ResizeHandler({
- plotData,
- volumeBars,
- setMaximizePlot,
- });
- const layout_update = update.layout_update;
- const newPlotData = update.plotData;
- const volume_update = update.volume_update;
-
- if (Object.keys(layout_update).length > 0) {
- setPlotData(newPlotData);
- setVolumeBars(volume_update);
- Plotly.relayout(plotDiv, layout_update);
- }
- });
-
- if (theme !== "dark") {
- setChangeTheme(true);
- }
-
- const traceTypes = originalData.data.map(
- (trace) => trace.type === "candlestick",
- );
- if (
- (originalData.data[0]?.x !== undefined &&
- originalData.data[0]?.x.length <= 1000) ||
- !traceTypes.includes(true)
- )
- return;
- setModal({
- name: "alertDialog",
- data: {
- title: "Warning",
- content: `Data has been truncated to 1000 points for performance reasons.
+ const posthog = usePostHog();
+
+ useEffect(() => {
+ if (posthog) posthog.capture("chart", info);
+ }, []);
+
+ json.layout.width = undefined;
+ json.layout.height = undefined;
+ if (json.layout?.title?.text) {
+ json.layout.title.text = "";
+ }
+
+ const [originalData, setOriginalData] = useState(json);
+ const [barButtons, setModeBarButtons] = useState({});
+ const [LogYaxis, setLogYaxis] = useState(false);
+ const [chartTitle, setChartTitle] = useState(title);
+ const [axesTitles, setAxesTitles] = useState({});
+ const [plotLoaded, setPlotLoaded] = useState(false);
+ const [modal, setModal] = useState({ name: "" });
+ const [loading, setLoading] = useState(false);
+ const [plotDiv, setPlotDiv] = useState(null);
+ const [volumeBars, setVolumeBars] = useState({ old_nticks: {} });
+ const [maximizePlot, setMaximizePlot] = useState(false);
+ const [downloadFinished, setDownloadFinished] = useState(false);
+ const [dateSliced, setDateSliced] = useState(false);
+
+ const [plotData, setPlotData] = useState(originalData);
+ const [annotations, setAnnotations] = useState([]);
+ const [changeTheme, setChangeTheme] = useState(false);
+ const [darkMode, setDarkMode] = useState(true);
+ const [autoScale, setAutoScaling] = useState(false);
+ const [changeColor, setChangeColor] = useState(false);
+ const [colorActive, setColorActive] = useState(false);
+ const [onAnnotationClick, setOnAnnotationClick] = useState({});
+ const [ohlcAnnotation, setOhlcAnnotation] = useState([]);
+ const [yaxisFixedRange, setYaxisFixedRange] = useState([]);
+
+ const onClose = () => setModal({ name: "" });
+
+ // @ts-ignore
+ function onDeleteAnnotation(annotation) {
+ console.log("onDeleteAnnotation", annotation);
+ const index = plotData?.layout?.annotations?.findIndex(
+ (a: any) => a.text === annotation.text,
+ );
+ console.log("index", index);
+ if (index > -1) {
+ plotData?.layout?.annotations?.splice(index, 1);
+ setPlotData({ ...plotData });
+ setAnnotations(plotData?.layout?.annotations);
+ }
+ }
+
+ // @ts-ignore
+ function onAddAnnotation(data) {
+ init_annotation({
+ plotData,
+ popupData: data,
+ setPlotData,
+ setModal,
+ setOnAnnotationClick,
+ setAnnotations,
+ onAnnotationClick,
+ ohlcAnnotation,
+ setOhlcAnnotation,
+ annotations,
+ plotDiv,
+ });
+ }
+
+ useEffect(() => {
+ if (downloadFinished) {
+ setModal({ name: "downloadFinished" });
+ setDownloadFinished(false);
+ }
+ }, [downloadFinished]);
+
+ useEffect(() => {
+ if (axesTitles && Object.keys(axesTitles).length > 0) {
+ Object.keys(axesTitles).forEach((k) => {
+ plotData.layout[k].title = {
+ ...(plotData.layout[k].title || {}),
+ text: axesTitles[k],
+ };
+ plotData.layout[k].showticklabels = true;
+ });
+ setAxesTitles({});
+ }
+ }, [axesTitles]);
+
+ function onChangeColor(color) {
+ // updates the color of the last added shape
+ // this function is called when the color picker is used
+ // if there are no shapes, we remove the color picker
+ const shapes = plotDiv.layout.shapes;
+ if (!shapes || shapes.length === 0) {
+ return;
+ }
+ // we change last added shape color
+ const last_shape = shapes[shapes.length - 1];
+ last_shape.line.color = color;
+ Plotly.update(plotDiv, {}, { shapes: shapes });
+ }
+
+ function button_pressed(title, active = false) {
+ // changes the style of the button when it is pressed
+ // title is the title of the button
+ // active is true if the button is active, false otherwise
+
+ const button =
+ barButtons[title] || document.querySelector(`[data-title="${title}"]`);
+ if (!active) {
+ button.style.border = "1px solid rgba(0, 151, 222, 1.0)";
+ button.style.borderRadius = "5px";
+ button.style.borderpadding = "5px";
+ button.style.boxShadow = "0 0 5px rgba(0, 151, 222, 1.0)";
+ } else {
+ button.style.border = "transparent";
+ button.style.boxShadow = "none";
+ }
+ setModeBarButtons({ ...barButtons, [title]: button });
+ }
+
+ function autoscaleButton() {
+ // We need to check if the button is active or not
+ const title = "Auto Scale (Ctrl+Shift+A)";
+ const button =
+ barButtons[title] || document.querySelector(`[data-title="${title}"]`);
+ let active = true;
+
+ if (button.style.border === "transparent") {
+ plotDiv.removeAllListeners("plotly_relayout");
+ active = false;
+ plotDiv.on(
+ "plotly_relayout",
+ non_blocking(async function (eventdata) {
+ if (eventdata["xaxis.range[0]"] === undefined) return;
+ if (dateSliced) {
+ const data = { ...originalData };
+ await DynamicLoad({
+ event: eventdata,
+ figure: data,
+ }).then(async (to_update) => {
+ setPlotData(to_update);
+ Plotly.react(plotDiv, to_update.data, to_update.layout);
+ const scaled = await autoScaling(eventdata, plotDiv);
+ setYaxisFixedRange(scaled.yaxis_fixedrange);
+ Plotly.update(plotDiv, {}, scaled.to_update);
+ });
+ } else {
+ const scaled = await autoScaling(eventdata, plotDiv);
+ setYaxisFixedRange(scaled.yaxis_fixedrange);
+ Plotly.update(plotDiv, {}, scaled.to_update);
+ }
+ }, 100),
+ );
+ }
+ // If the button isn't active, we remove the listener so
+ // the graphs don't autoscale anymore
+ else {
+ plotDiv.removeAllListeners("plotly_relayout");
+ yaxisFixedRange.forEach((yaxis) => {
+ plotDiv.layout[yaxis].fixedrange = false;
+ });
+ setYaxisFixedRange([]);
+ if (dateSliced) {
+ plotDiv.on(
+ "plotly_relayout",
+ non_blocking(async function (eventdata) {
+ if (eventdata["xaxis.range[0]"] === undefined) return;
+ const data = { ...originalData };
+ await DynamicLoad({
+ event: eventdata,
+ figure: data,
+ }).then(async (to_update) => {
+ setPlotData(to_update);
+ Plotly.react(plotDiv, to_update.data, to_update.layout);
+ });
+ }, 100),
+ );
+ }
+ }
+
+ button_pressed(title, active);
+ }
+
+ function changecolorButton() {
+ // We need to check if the button is active or not
+ const title = "Edit Color (Ctrl+E)";
+ const button =
+ barButtons[title] || document.querySelector(`[data-title="${title}"]`);
+ let active = true;
+
+ if (button.style.border === "transparent") {
+ active = false;
+ }
+
+ setColorActive(!active);
+ button_pressed(title, active);
+ }
+
+ useEffect(() => {
+ if (autoScale) {
+ const scale = !autoScale;
+ console.log("activateAutoScale", scale);
+ autoscaleButton();
+ setAutoScaling(false);
+ }
+ }, [autoScale]);
+
+ useEffect(() => {
+ if (changeColor) {
+ changecolorButton();
+ setChangeColor(false);
+ }
+ }, [changeColor]);
+
+ useEffect(() => {
+ if (changeTheme) {
+ try {
+ console.log("changeTheme", changeTheme);
+ const TRACES = plotData?.data.filter((trace) =>
+ trace?.name?.startsWith("Volume"),
+ );
+ const darkmode = !darkMode;
+
+ window.document.body.style.backgroundColor = darkmode ? "#000" : "#fff";
+
+ plotData.layout.font = {
+ ...(plotData.layout.font || {}),
+ color: darkmode ? "#fff" : "#000",
+ };
+
+ const changeIcon = darkmode ? ICONS.sunIcon : ICONS.moonIcon;
+
+ document
+ .querySelector('[data-title="Change Theme"]')
+ .getElementsByTagName("path")[0]
+ .setAttribute("d", changeIcon.path);
+
+ document
+ .querySelector('[data-title="Change Theme"]')
+ .getElementsByTagName("svg")[0]
+ .setAttribute("viewBox", changeIcon.viewBox);
+
+ const volumeColorsDark = {
+ "#009600": "#00ACFF",
+ "#c80000": "#e4003a",
+ };
+ const volumeColorsLight = {
+ "#e4003a": "#c80000",
+ "#00ACFF": "#009600",
+ };
+
+ const volumeColors = darkmode ? volumeColorsDark : volumeColorsLight;
+
+ TRACES.forEach((trace) => {
+ if (trace.type === "bar")
+ trace.marker.color = trace.marker.color.map((color) => {
+ return volumeColors[color] || color;
+ });
+ });
+ plotData.layout.template = darkmode
+ ? DARK_CHARTS_TEMPLATE
+ : LIGHT_CHARTS_TEMPLATE;
+ setPlotData({ ...plotData });
+ Plotly.react(plotDiv, plotData.data, plotData.layout);
+ setDarkMode(darkmode);
+ setChangeTheme(false);
+ } catch (e) {
+ console.log("error", e);
+ }
+ }
+ }, [changeTheme]);
+
+ useEffect(() => {
+ if (plotLoaded) {
+ setDarkMode(true);
+ setAutoScaling(false);
+ const captureButtons = [
+ "Download CSV",
+ "Download Chart as Image",
+ "Overlay chart from CSV",
+ "Add Text",
+ "Change Titles",
+ "Auto Scale (Ctrl+Shift+A)",
+ "Reset Axes",
+ ];
+ const autoscale = document.querySelector('[data-title="Autoscale"]');
+ if (autoscale) {
+ autoscale
+ .getElementsByTagName("path")