diff options
author | teh_coderer <me@tehcoderer.com> | 2023-06-05 13:03:08 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-05 17:03:08 +0000 |
commit | e040e9e6c4e52d8f84c94dd7d2bd59d8906adfcd (patch) | |
tree | ad018acb1506b2b58644758c0c9e1ffbcd3a883b | |
parent | b81aa22385261a4a9054208b9bcb01174afadc76 (diff) |
fix wrong default for `yaxisFixedRange` (#5106)
-rw-r--r-- | frontend-components/plotly/src/components/Chart.tsx | 1264 | ||||
-rw-r--r-- | openbb_terminal/core/plots/plotly.html | 24 |
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") |