File size: 4,621 Bytes
09801ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import React, { useEffect, useRef } from 'react';

interface PlotlyChartProps {
    data: any[];
    layout: any;
    config?: any;
}

/**
 * PlotlyChart component - Renders interactive Plotly charts
 * Uses dynamic import to avoid SSR issues
 */
const PlotlyChart: React.FC<PlotlyChartProps> = ({ data, layout, config }) => {
    // Detect theme - app uses light-theme class for light mode, absence = dark mode
    const [isDark, setIsDark] = React.useState(!document.documentElement.classList.contains('light-theme'));
    const containerRef = useRef<HTMLDivElement>(null);

    // Watch for theme changes on html element
    useEffect(() => {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.attributeName === 'class') {
                    setIsDark(!document.documentElement.classList.contains('light-theme'));
                }
            });
        });

        observer.observe(document.documentElement, {
            attributes: true,
            attributeFilter: ['class'],
        });

        return () => observer.disconnect();
    }, []);

    useEffect(() => {
        const renderChart = async () => {
            if (!containerRef.current) return;

            try {
                // Dynamic import of Plotly
                const Plotly = await import('plotly.js-dist-min');

                // Define theme colors based on current theme
                const themeColors = isDark ? {
                    text: '#e5e7eb',      // gray-200 - light text for dark mode
                    grid: 'rgba(255,255,255,0.1)',
                    bg: 'rgba(0,0,0,0)'   // transparent
                } : {
                    text: '#1f2937',      // gray-800 - dark text for light mode
                    grid: 'rgba(0,0,0,0.1)',
                    bg: 'rgba(0,0,0,0)'   // transparent
                };

                const defaultConfig = {
                    responsive: true,
                    displayModeBar: true,
                    modeBarButtonsToRemove: ['lasso2d', 'select2d'],
                    displaylogo: false,
                    ...config
                };

                // Merge and override layout for theme compatibility
                const enhancedLayout = {
                    ...layout,
                    autosize: true,
                    paper_bgcolor: themeColors.bg,
                    plot_bgcolor: themeColors.bg,
                    font: {
                        ...layout?.font,
                        color: themeColors.text
                    },
                    xaxis: {
                        ...layout?.xaxis,
                        gridcolor: themeColors.grid,
                        color: themeColors.text,
                        tickfont: { color: themeColors.text }
                    },
                    yaxis: {
                        ...layout?.yaxis,
                        gridcolor: themeColors.grid,
                        color: themeColors.text,
                        tickfont: { color: themeColors.text }
                    },
                    legend: {
                        ...layout?.legend,
                        font: {
                            ...layout?.legend?.font,
                            color: themeColors.text
                        }
                    },
                    title: layout?.title ? {
                        ...layout.title,
                        font: {
                            ...layout.title.font,
                            color: themeColors.text
                        }
                    } : undefined,
                    margin: { l: 50, r: 30, t: 40, b: 40, ...layout?.margin },
                };

                Plotly.default.newPlot(
                    containerRef.current,
                    data,
                    enhancedLayout,
                    defaultConfig
                );

                // Cleanup on unmount
                return () => {
                    if (containerRef.current) {
                        Plotly.default.purge(containerRef.current);
                    }
                };
            } catch (error) {
                console.error('Failed to render Plotly chart:', error);
            }
        };

        renderChart();
    }, [data, layout, config, isDark]);

    return (
        <div
            ref={containerRef}
            className="w-full min-h-[300px] rounded-lg overflow-hidden chart-fade-in"
            style={{ background: 'transparent' }}
        />
    );
};

export default PlotlyChart;