import React, { useMemo } from 'react'; import { Area, Canvas, colors, ContextMenuPlugin, GraphCustomFieldConfig, LegendDisplayMode, LegendPlugin, Line, Point, SeriesGeometry, Scale, TooltipPlugin, UPlotChart, ZoomPlugin, } from '@grafana/ui'; import { FieldConfig, FieldType, formattedValueToString, getColorFromHexRgbOrName, getTimeField, PanelProps, systemDateFormats, } from '@grafana/data'; import { Options } from './types'; import { alignAndSortDataFramesByFieldName } from './utils'; import { VizLayout } from './VizLayout'; import { Axis } from '@grafana/ui/src/components/uPlot/geometries/Axis'; import { timeFormatToTemplate } from '@grafana/ui/src/components/uPlot/utils'; interface GraphPanelProps extends PanelProps {} const TIME_FIELD_NAME = 'Time'; const timeStampsConfig = [ [3600 * 24 * 365, '{YYYY}', 7, '{YYYY}'], [3600 * 24 * 28, `{${timeFormatToTemplate(systemDateFormats.interval.month)}`, 7, '{MMM}\n{YYYY}'], [ 3600 * 24, `{${timeFormatToTemplate(systemDateFormats.interval.day)}`, 7, `${timeFormatToTemplate(systemDateFormats.interval.day)}\n${timeFormatToTemplate(systemDateFormats.interval.year)}`, ], [ 3600, `{${timeFormatToTemplate(systemDateFormats.interval.minute)}`, 4, `${timeFormatToTemplate(systemDateFormats.interval.minute)}\n${timeFormatToTemplate( systemDateFormats.interval.day )}`, ], [ 60, `{${timeFormatToTemplate(systemDateFormats.interval.second)}`, 4, `${timeFormatToTemplate(systemDateFormats.interval.second)}\n${timeFormatToTemplate( systemDateFormats.interval.day )}`, ], [ 1, `:{ss}`, 2, `:{ss}\n${timeFormatToTemplate(systemDateFormats.interval.day)} ${timeFormatToTemplate( systemDateFormats.interval.minute )}`, ], [ 1e-3, ':{ss}.{fff}', 2, `:{ss}.{fff}\n${timeFormatToTemplate(systemDateFormats.interval.day)} ${timeFormatToTemplate( systemDateFormats.interval.minute )}`, ], ]; const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1)); export const GraphPanel: React.FC = ({ data, timeRange, timeZone, width, height, options, onChangeTimeRange, }) => { const alignedData = useMemo(() => { if (!data || !data.series?.length) { return null; } return alignAndSortDataFramesByFieldName(data.series, TIME_FIELD_NAME); }, [data]); if (!alignedData) { return (

No data found in response

); } const geometries: React.ReactNode[] = []; const scales: React.ReactNode[] = []; const axes: React.ReactNode[] = []; let { timeIndex } = getTimeField(alignedData); if (timeIndex === undefined) { timeIndex = 0; // assuming first field represents x-domain scales.push(); } else { scales.push(); } axes.push(); let seriesIdx = 0; const uniqueScales: Record = {}; for (let i = 0; i < alignedData.fields.length; i++) { const seriesGeometry = []; const field = alignedData.fields[i]; const config = field.config as FieldConfig; const customConfig = config.custom; if (i === timeIndex || field.type !== FieldType.number) { continue; } const fmt = field.display ?? defaultFormatter; const scale = config.unit || '__fixed'; if (!uniqueScales[scale]) { uniqueScales[scale] = true; scales.push(); axes.push( formattedValueToString(fmt(v))} /> ); } const seriesColor = customConfig?.line.color && customConfig?.line.color.fixedColor ? getColorFromHexRgbOrName(customConfig.line.color.fixedColor) : colors[seriesIdx]; if (customConfig?.line?.show) { seriesGeometry.push( ); } if (customConfig?.points?.show) { seriesGeometry.push( ); } if (customConfig?.fill?.alpha) { seriesGeometry.push( ); } if (seriesGeometry.length > 1) { geometries.push( {seriesGeometry} ); } else { geometries.push(seriesGeometry); } seriesIdx++; } return ( {({ builder, getLayout }) => { const layout = getLayout(); // when all layout slots are ready we can calculate the canvas(actual viz) size const canvasSize = layout.isReady ? { width: width - (layout.left.width + layout.right.width), height: height - (layout.top.height + layout.bottom.height), } : { width: 0, height: 0 }; if (options.legend.isVisible) { builder.addSlot( options.legend.placement, ); } else { builder.clearSlot(options.legend.placement); } return ( {scales} {axes} {geometries} {builder.addSlot('canvas', ).render()} {/* TODO: */} {/**/} ); }} ); };