File size: 2,919 Bytes
0a8b8ba
d6c9678
 
 
0a8b8ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d6c9678
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a8b8ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d6c9678
 
 
f7fe834
 
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
import React, { useEffect, useRef, useState } from 'react'
import Plot from 'react-plotly.js'
import Plotly from 'plotly.js-dist-min'

function PlotFigure({ figure, title, subtitle = '', forceHideLegend = false, className = '', lazy = false }) {
  const containerRef = useRef(null)
  const [shouldRenderPlot, setShouldRenderPlot] = useState(() => !lazy)

  useEffect(() => {
    if (!lazy) {
      setShouldRenderPlot(true)
      return undefined
    }
    if (shouldRenderPlot) return undefined
    if (typeof window === 'undefined' || !('IntersectionObserver' in window)) {
      setShouldRenderPlot(true)
      return undefined
    }
    const target = containerRef.current
    if (!target) return undefined
    const observer = new IntersectionObserver(
      (entries) => {
        const isVisible = entries.some((entry) => entry.isIntersecting || entry.intersectionRatio > 0)
        if (isVisible) {
          setShouldRenderPlot(true)
          observer.disconnect()
        }
      },
      { rootMargin: '240px 0px', threshold: 0.01 },
    )
    observer.observe(target)
    return () => observer.disconnect()
  }, [lazy, shouldRenderPlot])

  if (!figure) {
    return <div className="empty-box">Grafico indisponivel.</div>
  }

  const data = (figure.data || []).map((trace) => {
    if (!forceHideLegend) return trace
    return { ...trace, showlegend: false }
  })

  const baseLayout = figure.layout || {}
  const { width: _ignoreWidth, ...layoutNoWidth } = baseLayout
  const safeAnnotations = Array.isArray(baseLayout.annotations)
    ? baseLayout.annotations.map((annotation) => {
        const { ax, ay, axref, ayref, arrowhead, arrowsize, arrowwidth, arrowcolor, standoff, ...clean } = annotation || {}
        return { ...clean, showarrow: false }
      })
    : baseLayout.annotations
  const layout = {
    ...layoutNoWidth,
    autosize: true,
    annotations: safeAnnotations,
    margin: baseLayout.margin || { t: 40, r: 20, b: 50, l: 50 },
  }
  if (forceHideLegend) {
    layout.showlegend = false
  }
  const plotHeight = layout.height ? `${layout.height}px` : '100%'
  const cardClassName = `plot-card ${className}`.trim()

  return (
    <div ref={containerRef} className={cardClassName}>
      {title || subtitle ? (
        <div className="plot-card-head">
          {title ? <h4 className="plot-card-title">{title}</h4> : null}
          {subtitle ? <div className="plot-card-subtitle">{subtitle}</div> : null}
        </div>
      ) : null}
      {shouldRenderPlot ? (
        <Plot
          data={data}
          layout={layout}
          config={{ responsive: true, displaylogo: false }}
          style={{ width: '100%', height: plotHeight, minHeight: '320px' }}
          useResizeHandler
          plotly={Plotly}
        />
      ) : (
        <div className="plot-lazy-placeholder">Carregando gráfico...</div>
      )}
    </div>
  )
}

export default React.memo(PlotFigure)