From 9adad76f5211b00257e255c5786ef8155ed03beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Oct 2018 22:24:18 +0200 Subject: [PATCH] wip: began first steps for a react graph component --- public/app/plugins/panel/graph2/module.tsx | 19 +-- public/app/types/index.ts | 11 +- public/app/types/panel.ts | 3 +- public/app/types/series.ts | 7 + public/app/viz/Graph.tsx | 126 ++++++++++++++++++ .../app/viz/state/getTimeSeriesViewModel.ts | 9 ++ 6 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 public/app/viz/Graph.tsx create mode 100644 public/app/viz/state/getTimeSeriesViewModel.ts diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index 7132efc9ac0..a984e6a2a6f 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -2,6 +2,9 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; +// Components +import { Graph } from 'app/viz/Graph'; + // Types import { PanelProps } from 'app/types'; @@ -22,21 +25,7 @@ export class Graph2 extends PureComponent { const { timeSeries } = this.props; let index = 0; - return ( - - - {timeSeries.map(series => { - return ( - - - - - - ); - })} - -
{series.target}{series.datapoints[0][0]}{series.datapoints[0][1]}
- ); + return ; } } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index f13a55b5361..8d827a6c286 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,15 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginMeta, Plugin, PluginsState } from './plugins'; -import { TimeRange, LoadingState, TimeSeries, DataQuery, DataQueryResponse, DataQueryOptions } from './series'; +import { + TimeRange, + LoadingState, + TimeSeries, + DataQuery, + DataQueryResponse, + DataQueryOptions, + TimeSeriesViewModel, +} from './series'; import { PanelProps } from './panel'; export { @@ -51,6 +59,7 @@ export { LoadingState, PanelProps, TimeSeries, + TimeSeriesViewModel, DataQuery, DataQueryResponse, DataQueryOptions, diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts index 8d12cb6ef21..5ece77fc5aa 100644 --- a/public/app/types/panel.ts +++ b/public/app/types/panel.ts @@ -1,6 +1,7 @@ -import { LoadingState, TimeSeries } from './series'; +import { LoadingState, TimeSeries, TimeRange } from './series'; export interface PanelProps { timeSeries: TimeSeries[]; + timeRange: TimeRange; loading: LoadingState; } diff --git a/public/app/types/series.ts b/public/app/types/series.ts index 006410a5757..dc1d1d7d393 100644 --- a/public/app/types/series.ts +++ b/public/app/types/series.ts @@ -25,6 +25,13 @@ export type TimeSeriesPoints = TimeSeriesValue[][]; export interface TimeSeries { target: string; datapoints: TimeSeriesPoints; + unit?: string; +} + +export interface TimeSeriesViewModel { + label: string; + color: string; + data: number[][]; } export interface DataQueryResponse { diff --git a/public/app/viz/Graph.tsx b/public/app/viz/Graph.tsx new file mode 100644 index 00000000000..ed638e7bd16 --- /dev/null +++ b/public/app/viz/Graph.tsx @@ -0,0 +1,126 @@ +// Libraries +import $ from 'jquery'; +import React, { PureComponent } from 'react'; +import { withSize } from 'react-sizeme'; +import 'vendor/flot/jquery.flot'; +import 'vendor/flot/jquery.flot.time'; + +// Types +import TimeSeries from 'app/core/time_series2'; +import { TimeRange } from 'app/types'; + +// Copied from graph.ts +function time_format(ticks, min, max) { + if (min && max && ticks) { + const range = max - min; + const secPerTick = range / ticks / 1000; + const oneDay = 86400000; + const oneYear = 31536000000; + + if (secPerTick <= 45) { + return '%H:%M:%S'; + } + if (secPerTick <= 7200 || range <= oneDay) { + return '%H:%M'; + } + if (secPerTick <= 80000) { + return '%m/%d %H:%M'; + } + if (secPerTick <= 2419200 || range <= oneYear) { + return '%m/%d'; + } + return '%Y-%m'; + } + + return '%H:%M'; +} + +const FLOT_OPTIONS = { + legend: { + show: false, + }, + series: { + lines: { + linewidth: 1, + zero: false, + }, + shadowSize: 0, + }, + grid: { + minBorderMargin: 0, + markings: [], + backgroundColor: null, + borderWidth: 0, + // hoverable: true, + clickable: true, + color: '#a1a1a1', + margin: { left: 0, right: 0 }, + labelMarginX: 0, + }, +}; + +interface GraphProps { + timeSeries: TimeSeries[]; + timeRange: TimeRange; + size?: { width: number; height: number }; +} + +export class Graph extends PureComponent { + element: any; + + componentDidUpdate(prevProps: GraphProps) { + if ( + prevProps.timeSeries !== this.props.timeSeries || + prevProps.timeRange !== this.props.timeRange || + prevProps.size !== this.props.size + ) { + this.draw(); + } + } + + componentDidMount() { + this.draw(); + } + + draw() { + const { size, timeSeries, timeRange } = this.props; + + const data = timeSeries.map((ts: TimeSeries) => ({ + color: ts.color, + label: ts.label, + data: ts.getFlotPairs('null'), + })); + + const ticks = (size.width || 0) / 100; + const min = timeRange.from.valueOf(); + const max = timeRange.to.valueOf(); + + const dynamicOptions = { + xaxis: { + mode: 'time', + min: min, + max: max, + label: 'Datetime', + ticks: ticks, + timeformat: time_format(ticks, min, max), + }, + }; + + const options = { + ...FLOT_OPTIONS, + ...dynamicOptions, + }; + + $.plot(this.element, data, options); + } + + render() { + return ( +
+
(this.element = e)} /> +
+ ); + } +} + +export default withSize()(Graph); diff --git a/public/app/viz/state/getTimeSeriesViewModel.ts b/public/app/viz/state/getTimeSeriesViewModel.ts new file mode 100644 index 00000000000..9ad2216d135 --- /dev/null +++ b/public/app/viz/state/getTimeSeriesViewModel.ts @@ -0,0 +1,9 @@ +import colors from 'app/core/utils/colors'; +import { TimeSeries, TimeSeriesViewModel } from 'app/types'; + +interface Options { + ts: TimeSeries; + seriesIndex: number; +} + +export function getTimeSeriesViewModel(ts: TimeSeries): TimeSeriesViewModel {}