mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
* First attempt * Get rid of time range as config invalidation dependency * GraphNG class refactor * Get rid of DataFrame dependency from Plot component, get rid of usePlotData context, rely on XYMatchers for data inspection from within plugins * Bring back legend * Fix Sparkline * Fix Sparkline * Sparkline update * Explore update * fix * BarChart refactor to class * Tweaks * TS fix * Fix tests * Tests * Update packages/grafana-ui/src/components/uPlot/utils.ts * Update public/app/plugins/panel/timeseries/plugins/ContextMenuPlugin.tsx * GraphNG: unified legend for BarChart, GraphNG & other uPlot based visualizations (#31175) * Legend experiment * Nits
103 lines
2.3 KiB
TypeScript
103 lines
2.3 KiB
TypeScript
import { DataFrame, Field, FieldMatcher, FieldType, getFieldDisplayName } from '@grafana/data';
|
|
import { XYDimensionConfig } from './types';
|
|
|
|
// TODO: fix import
|
|
import { XYFieldMatchers } from '@grafana/ui/src/components/GraphNG/types';
|
|
|
|
export enum DimensionError {
|
|
NoData,
|
|
BadFrameSelection,
|
|
XNotFound,
|
|
}
|
|
|
|
export interface XYDimensions {
|
|
frame: DataFrame; // matches order from configs, excluds non-graphable values
|
|
x: Field;
|
|
fields: XYFieldMatchers;
|
|
error?: DimensionError;
|
|
hasData?: boolean;
|
|
hasTime?: boolean;
|
|
}
|
|
|
|
export function isGraphable(field: Field) {
|
|
return field.type === FieldType.number;
|
|
}
|
|
|
|
export function getXYDimensions(cfg?: XYDimensionConfig, data?: DataFrame[]): XYDimensions {
|
|
if (!data || !data.length) {
|
|
return { error: DimensionError.NoData } as XYDimensions;
|
|
}
|
|
if (!cfg) {
|
|
cfg = {
|
|
frame: 0,
|
|
};
|
|
}
|
|
|
|
let frame = data[cfg.frame ?? 0];
|
|
if (!frame) {
|
|
return { error: DimensionError.BadFrameSelection } as XYDimensions;
|
|
}
|
|
|
|
let xIndex = -1;
|
|
for (let i = 0; i < frame.fields.length; i++) {
|
|
const f = frame.fields[i];
|
|
if (cfg.x && cfg.x === getFieldDisplayName(f, frame, data)) {
|
|
xIndex = i;
|
|
break;
|
|
}
|
|
if (isGraphable(f) && !cfg.x) {
|
|
xIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
let hasTime = false;
|
|
const x = frame.fields[xIndex];
|
|
const fields: Field[] = [x];
|
|
for (const f of frame.fields) {
|
|
if (f.type === FieldType.time) {
|
|
hasTime = true;
|
|
}
|
|
if (f === x || !isGraphable(f)) {
|
|
continue;
|
|
}
|
|
if (cfg.exclude) {
|
|
const name = getFieldDisplayName(f, frame, data);
|
|
if (cfg.exclude.includes(name)) {
|
|
continue;
|
|
}
|
|
}
|
|
fields.push(f);
|
|
}
|
|
|
|
return {
|
|
x,
|
|
fields: {
|
|
x: getSimpleFieldMatcher(x),
|
|
y: getSimpleFieldNotMatcher(x), // Not x
|
|
},
|
|
frame: {
|
|
...frame,
|
|
fields,
|
|
},
|
|
hasData: frame.fields.length > 0,
|
|
hasTime,
|
|
};
|
|
}
|
|
|
|
function getSimpleFieldMatcher(f: Field): FieldMatcher {
|
|
if (!f) {
|
|
return () => false;
|
|
}
|
|
// the field may change if sorted
|
|
return (field) => f === field || !!(f.state && f.state === field.state);
|
|
}
|
|
|
|
function getSimpleFieldNotMatcher(f: Field): FieldMatcher {
|
|
if (!f) {
|
|
return () => false;
|
|
}
|
|
const m = getSimpleFieldMatcher(f);
|
|
return (field) => !m(field, undefined as any, undefined as any);
|
|
}
|