|
| 1 | +import * as am5 from '@amcharts/amcharts5'; |
| 2 | +import * as am5stock from '@amcharts/amcharts5/stock'; |
| 3 | +import * as am5xy from '@amcharts/amcharts5/xy'; |
| 4 | + |
| 5 | +import React, { type PropsWithChildren, useEffect, useState } from 'react'; |
| 6 | +import type { StockChart } from './StockChart'; |
| 7 | + |
| 8 | +type MainPanelProps = PropsWithChildren<Partial<StockChart>>; |
| 9 | + |
| 10 | +export type MainPanel = { |
| 11 | + mainPanel: am5stock.StockPanel | null; |
| 12 | + dateAxis: am5xy.GaplessDateAxis<am5xy.AxisRenderer> | null; |
| 13 | + valueAxis: am5xy.ValueAxis<am5xy.AxisRenderer> | null; |
| 14 | + valueLegend: am5stock.StockLegend | null; |
| 15 | +} & StockChart; |
| 16 | + |
| 17 | +export default function MainPanel({ |
| 18 | + chartRoot, |
| 19 | + stockChart, |
| 20 | + children, |
| 21 | +}: MainPanelProps) { |
| 22 | + const [mainPanel, setMainPanel] = useState<MainPanel['mainPanel']>(null); |
| 23 | + const [dateAxis, setDateAxis] = useState<MainPanel['dateAxis']>(null); |
| 24 | + const [valueAxis, setValueAxis] = useState<MainPanel['valueAxis']>(null); |
| 25 | + const [valueLegend, setValueLegend] = |
| 26 | + useState<MainPanel['valueLegend']>(null); |
| 27 | + |
| 28 | + const childrenWithProps = React.Children.map(children, (child) => { |
| 29 | + if (React.isValidElement<MainPanel>(child)) { |
| 30 | + return React.cloneElement(child, { |
| 31 | + chartRoot, |
| 32 | + stockChart, |
| 33 | + mainPanel, |
| 34 | + dateAxis, |
| 35 | + valueAxis, |
| 36 | + valueLegend, |
| 37 | + }); |
| 38 | + } |
| 39 | + return child; |
| 40 | + }); |
| 41 | + |
| 42 | + useEffect(() => { |
| 43 | + if (!chartRoot || !stockChart) return; |
| 44 | + const newPanel = stockChart.panels.push( |
| 45 | + am5stock.StockPanel.new(chartRoot, { |
| 46 | + wheelY: 'zoomX', |
| 47 | + panX: true, |
| 48 | + panY: true, |
| 49 | + }), |
| 50 | + ); |
| 51 | + setMainPanel(newPanel); |
| 52 | + |
| 53 | + const newDateAxis = newPanel.xAxes.push( |
| 54 | + am5xy.GaplessDateAxis.new(chartRoot, { |
| 55 | + baseInterval: { |
| 56 | + timeUnit: 'minute', |
| 57 | + count: 1, |
| 58 | + }, |
| 59 | + renderer: am5xy.AxisRendererX.new(chartRoot, { |
| 60 | + minorGridEnabled: true, |
| 61 | + }), |
| 62 | + tooltip: am5.Tooltip.new(chartRoot, {}), |
| 63 | + }), |
| 64 | + ); |
| 65 | + |
| 66 | + setDateAxis(newDateAxis); |
| 67 | + |
| 68 | + const newValueLegend = newPanel.plotContainer.children.push( |
| 69 | + am5stock.StockLegend.new(chartRoot, { |
| 70 | + stockChart, |
| 71 | + }), |
| 72 | + ); |
| 73 | + |
| 74 | + setValueLegend(newValueLegend); |
| 75 | + |
| 76 | + const newValueAxis = newPanel.yAxes.push( |
| 77 | + am5xy.ValueAxis.new(chartRoot, { |
| 78 | + renderer: am5xy.AxisRendererY.new(chartRoot, { |
| 79 | + pan: 'zoom', |
| 80 | + }), |
| 81 | + extraMin: 0.1, |
| 82 | + tooltip: am5.Tooltip.new(chartRoot, {}), |
| 83 | + numberFormat: '#,###.00', |
| 84 | + extraTooltipPrecision: 2, |
| 85 | + }), |
| 86 | + ); |
| 87 | + |
| 88 | + setValueAxis(newValueAxis); |
| 89 | + |
| 90 | + newPanel.set( |
| 91 | + 'cursor', |
| 92 | + am5xy.XYCursor.new(chartRoot, { |
| 93 | + yAxis: newValueAxis, |
| 94 | + xAxis: newDateAxis, |
| 95 | + snapToSeries: newValueAxis.series, |
| 96 | + snapToSeriesBy: 'y!', |
| 97 | + }), |
| 98 | + ); |
| 99 | + |
| 100 | + return () => { |
| 101 | + newPanel.dispose(); |
| 102 | + }; |
| 103 | + }, [chartRoot, stockChart]); |
| 104 | + |
| 105 | + return childrenWithProps; |
| 106 | +} |
0 commit comments