Canvas: Add universal data link support (#84142)

pull/84405/head
Nathan Marrs 1 year ago committed by GitHub
parent 0fe5b62fa5
commit 9573c13223
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2940
      devenv/dev-dashboards/panel-canvas/canvas-datalinks.json
  2. 1
      devenv/jsonnet/dev-dashboards.libsonnet
  3. 2
      public/app/features/canvas/element.ts
  4. 34
      public/app/features/canvas/elements/button.tsx
  5. 8
      public/app/features/canvas/elements/droneFront.tsx
  6. 8
      public/app/features/canvas/elements/droneSide.tsx
  7. 24
      public/app/features/canvas/elements/droneTop.tsx
  8. 36
      public/app/features/canvas/elements/ellipse.tsx
  9. 26
      public/app/features/canvas/elements/icon.tsx
  10. 26
      public/app/features/canvas/elements/metricValue.tsx
  11. 26
      public/app/features/canvas/elements/rectangle.tsx
  12. 22
      public/app/features/canvas/elements/server/server.tsx
  13. 20
      public/app/features/canvas/elements/text.tsx
  14. 14
      public/app/features/canvas/elements/windTurbine.tsx
  15. 2
      public/app/features/canvas/runtime/element.tsx
  16. 154
      public/app/plugins/panel/canvas/utils.ts

File diff suppressed because it is too large Load Diff

@ -21,6 +21,7 @@
"barchart-tooltips-legends": (import '../dev-dashboards/panel-barchart/barchart-tooltips-legends.json'),
"candlestick": (import '../dev-dashboards/panel-candlestick/candlestick.json'),
"canvas-connection-examples": (import '../dev-dashboards/panel-canvas/canvas-connection-examples.json'),
"canvas-datalinks": (import '../dev-dashboards/panel-canvas/canvas-datalinks.json'),
"canvas-examples": (import '../dev-dashboards/panel-canvas/canvas-examples.json'),
"color_modes": (import '../dev-dashboards/panel-common/color_modes.json'),
"config-from-query": (import '../dev-dashboards/transforms/config-from-query.json'),

@ -72,7 +72,7 @@ export interface CanvasElementItem<TConfig = any, TData = any> extends RegistryI
/** The default width/height to use when adding */
defaultSize?: Placement;
prepareData?: (ctx: DimensionContext, cfg: TConfig) => TData;
prepareData?: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<TConfig>) => TData;
/** Component used to draw */
display: ComponentType<CanvasElementProps<TConfig, TData>>;

@ -13,7 +13,7 @@ import { ButtonStyleConfig, ButtonStyleEditor } from 'app/plugins/panel/canvas/e
import { callApi } from 'app/plugins/panel/canvas/editor/element/utils';
import { HttpRequestMethod } from 'app/plugins/panel/canvas/panelcfg.gen';
import { CanvasElementItem, CanvasElementProps, defaultLightTextColor } from '../element';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps, defaultLightTextColor } from '../element';
import { Align, TextConfig, TextData } from '../types';
interface ButtonData extends Omit<TextData, 'valign'> {
@ -130,30 +130,32 @@ export const buttonItem: CanvasElementItem<ButtonConfig, ButtonData> = {
}),
// Called when data changes
prepareData: (ctx: DimensionContext, cfg: ButtonConfig) => {
const getCfgApi = () => {
if (cfg?.api) {
cfg.api = {
...cfg.api,
method: cfg.api.method ?? defaultApiConfig.method,
contentType: cfg.api.contentType ?? defaultApiConfig.contentType,
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<ButtonConfig>) => {
const buttonConfig = elementOptions.config;
const getAPIConfig = () => {
if (buttonConfig?.api) {
buttonConfig.api = {
...buttonConfig.api,
method: buttonConfig.api.method ?? defaultApiConfig.method,
contentType: buttonConfig.api.contentType ?? defaultApiConfig.contentType,
};
return cfg.api;
return buttonConfig.api;
}
return undefined;
};
const data: ButtonData = {
text: cfg?.text ? ctx.getText(cfg.text).value() : '',
align: cfg.align ?? Align.Center,
size: cfg.size ?? 14,
api: getCfgApi(),
style: cfg?.style ?? defaultStyleConfig,
text: buttonConfig?.text ? dimensionContext.getText(buttonConfig.text).value() : '',
align: buttonConfig?.align ?? Align.Center,
size: buttonConfig?.size ?? 14,
api: getAPIConfig(),
style: buttonConfig?.style ?? defaultStyleConfig,
};
if (cfg.color) {
data.color = ctx.getColor(cfg.color).value();
if (buttonConfig?.color) {
data.color = dimensionContext.getColor(buttonConfig.color).value();
}
return data;

@ -7,7 +7,7 @@ import { useStyles2 } from '@grafana/ui';
import { DimensionContext } from 'app/features/dimensions';
import { ScalarDimensionEditor } from 'app/features/dimensions/editors';
import { CanvasElementItem, CanvasElementProps, defaultBgColor } from '../element';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps, defaultBgColor } from '../element';
interface DroneFrontData {
rollAngle?: number;
@ -97,9 +97,11 @@ export const droneFrontItem: CanvasElementItem = {
}),
// Called when data changes
prepareData: (ctx: DimensionContext, cfg: DroneFrontConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<DroneFrontConfig>) => {
const droneFrontConfig = elementOptions.config;
const data: DroneFrontData = {
rollAngle: cfg?.rollAngle ? ctx.getScalar(cfg.rollAngle).value() : 0,
rollAngle: droneFrontConfig?.rollAngle ? dimensionContext.getScalar(droneFrontConfig.rollAngle).value() : 0,
};
return data;

@ -7,7 +7,7 @@ import { useStyles2 } from '@grafana/ui';
import { DimensionContext } from 'app/features/dimensions';
import { ScalarDimensionEditor } from 'app/features/dimensions/editors';
import { CanvasElementItem, CanvasElementProps, defaultBgColor } from '../element';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps, defaultBgColor } from '../element';
interface DroneSideData {
pitchAngle?: number;
@ -96,9 +96,11 @@ export const droneSideItem: CanvasElementItem = {
}),
// Called when data changes
prepareData: (ctx: DimensionContext, cfg: DroneSideConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<DroneSideConfig>) => {
const droneSideConfig = elementOptions.config;
const data: DroneSideData = {
pitchAngle: cfg?.pitchAngle ? ctx.getScalar(cfg.pitchAngle).value() : 0,
pitchAngle: droneSideConfig?.pitchAngle ? dimensionContext.getScalar(droneSideConfig.pitchAngle).value() : 0,
};
return data;

@ -7,7 +7,7 @@ import { useStyles2 } from '@grafana/ui';
import { DimensionContext } from 'app/features/dimensions';
import { ScalarDimensionEditor } from 'app/features/dimensions/editors';
import { CanvasElementItem, CanvasElementProps, defaultBgColor } from '../element';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps, defaultBgColor } from '../element';
interface DroneTopData {
bRightRotorRPM?: number;
@ -102,13 +102,23 @@ export const droneTopItem: CanvasElementItem = {
}),
// Called when data changes
prepareData: (ctx: DimensionContext, cfg: DroneTopConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<DroneTopConfig>) => {
const droneTopConfig = elementOptions.config;
const data: DroneTopData = {
bRightRotorRPM: cfg?.bRightRotorRPM ? ctx.getScalar(cfg.bRightRotorRPM).value() : 0,
bLeftRotorRPM: cfg?.bLeftRotorRPM ? ctx.getScalar(cfg.bLeftRotorRPM).value() : 0,
fRightRotorRPM: cfg?.fRightRotorRPM ? ctx.getScalar(cfg.fRightRotorRPM).value() : 0,
fLeftRotorRPM: cfg?.fLeftRotorRPM ? ctx.getScalar(cfg.fLeftRotorRPM).value() : 0,
yawAngle: cfg?.yawAngle ? ctx.getScalar(cfg.yawAngle).value() : 0,
bRightRotorRPM: droneTopConfig?.bRightRotorRPM
? dimensionContext.getScalar(droneTopConfig.bRightRotorRPM).value()
: 0,
bLeftRotorRPM: droneTopConfig?.bLeftRotorRPM
? dimensionContext.getScalar(droneTopConfig.bLeftRotorRPM).value()
: 0,
fRightRotorRPM: droneTopConfig?.fRightRotorRPM
? dimensionContext.getScalar(droneTopConfig.fRightRotorRPM).value()
: 0,
fLeftRotorRPM: droneTopConfig?.fLeftRotorRPM
? dimensionContext.getScalar(droneTopConfig.fLeftRotorRPM).value()
: 0,
yawAngle: droneTopConfig?.yawAngle ? dimensionContext.getScalar(droneTopConfig.yawAngle).value() : 0,
};
return data;

@ -8,7 +8,13 @@ import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimen
import { TextDimensionEditor } from 'app/features/dimensions/editors/TextDimensionEditor';
import { getDataLinks } from 'app/plugins/panel/canvas/utils';
import { CanvasElementItem, CanvasElementProps, defaultBgColor, defaultTextColor } from '../element';
import {
CanvasElementItem,
CanvasElementOptions,
CanvasElementProps,
defaultBgColor,
defaultTextColor,
} from '../element';
import { Align, VAlign, EllipseConfig, EllipseData } from '../types';
class EllipseDisplay extends PureComponent<CanvasElementProps<EllipseConfig, EllipseData>> {
@ -80,26 +86,28 @@ export const ellipseItem: CanvasElementItem<EllipseConfig, EllipseData> = {
},
}),
prepareData: (ctx: DimensionContext, cfg: EllipseConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<EllipseConfig>) => {
const ellipseConfig = elementOptions.config;
const data: EllipseData = {
width: cfg.width,
text: cfg.text ? ctx.getText(cfg.text).value() : '',
align: cfg.align ?? Align.Center,
valign: cfg.valign ?? VAlign.Middle,
size: cfg.size,
width: ellipseConfig?.width,
text: ellipseConfig?.text ? dimensionContext.getText(ellipseConfig.text).value() : '',
align: ellipseConfig?.align ?? Align.Center,
valign: ellipseConfig?.valign ?? VAlign.Middle,
size: ellipseConfig?.size,
};
if (cfg.backgroundColor) {
data.backgroundColor = ctx.getColor(cfg.backgroundColor).value();
if (ellipseConfig?.backgroundColor) {
data.backgroundColor = dimensionContext.getColor(ellipseConfig.backgroundColor).value();
}
if (cfg.borderColor) {
data.borderColor = ctx.getColor(cfg.borderColor).value();
if (ellipseConfig?.borderColor) {
data.borderColor = dimensionContext.getColor(ellipseConfig.borderColor).value();
}
if (cfg.color) {
data.color = ctx.getColor(cfg.color).value();
if (ellipseConfig?.color) {
data.color = dimensionContext.getColor(ellipseConfig.color).value();
}
data.links = getDataLinks(ctx, cfg, data.text);
data.links = getDataLinks(dimensionContext, elementOptions, data.text);
return data;
},

@ -2,13 +2,15 @@ import { css } from '@emotion/css';
import { isString } from 'lodash';
import React, { CSSProperties } from 'react';
import { LinkModel } from '@grafana/data';
import { ColorDimensionConfig, ResourceDimensionConfig, ResourceDimensionMode } from '@grafana/schema';
import { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG';
import { getPublicOrAbsoluteUrl } from 'app/features/dimensions';
import { DimensionContext } from 'app/features/dimensions/context';
import { ColorDimensionEditor, ResourceDimensionEditor } from 'app/features/dimensions/editors';
import { getDataLinks } from 'app/plugins/panel/canvas/utils';
import { CanvasElementItem, CanvasElementProps, defaultBgColor } from '../element';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps, defaultBgColor } from '../element';
import { LineConfig } from '../types';
export interface IconConfig {
@ -22,6 +24,7 @@ interface IconData {
fill: string;
strokeColor?: string;
stroke?: number;
links?: LinkModel[];
}
// When a stoke is defined, we want the path to be in page units
@ -80,10 +83,12 @@ export const iconItem: CanvasElementItem<IconConfig, IconData> = {
}),
// Called when data changes
prepareData: (ctx: DimensionContext, cfg: IconConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<IconConfig>) => {
const iconConfig = elementOptions.config;
let path: string | undefined = undefined;
if (cfg.path) {
path = ctx.getResource(cfg.path).value();
if (iconConfig?.path) {
path = dimensionContext.getResource(iconConfig.path).value();
}
if (!path || !isString(path)) {
path = getPublicOrAbsoluteUrl('img/icons/unicons/question-circle.svg');
@ -91,15 +96,18 @@ export const iconItem: CanvasElementItem<IconConfig, IconData> = {
const data: IconData = {
path,
fill: cfg.fill ? ctx.getColor(cfg.fill).value() : defaultBgColor,
fill: iconConfig?.fill ? dimensionContext.getColor(iconConfig.fill).value() : defaultBgColor,
};
if (cfg.stroke?.width && cfg.stroke.color) {
if (cfg.stroke.width > 0) {
data.stroke = cfg.stroke?.width;
data.strokeColor = ctx.getColor(cfg.stroke.color).value();
if (iconConfig?.stroke?.width && iconConfig?.stroke.color) {
if (iconConfig.stroke.width > 0) {
data.stroke = iconConfig.stroke?.width;
data.strokeColor = dimensionContext.getColor(iconConfig.stroke.color).value();
}
}
data.links = getDataLinks(dimensionContext, elementOptions, data.path);
return data;
},

@ -13,7 +13,13 @@ import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimen
import { TextDimensionEditor } from 'app/features/dimensions/editors/TextDimensionEditor';
import { getDataLinks } from 'app/plugins/panel/canvas/utils';
import { CanvasElementItem, CanvasElementProps, defaultBgColor, defaultTextColor } from '../element';
import {
CanvasElementItem,
CanvasElementOptions,
CanvasElementProps,
defaultBgColor,
defaultTextColor,
} from '../element';
import { ElementState } from '../runtime/element';
import { Align, TextConfig, TextData, VAlign } from '../types';
@ -171,19 +177,21 @@ export const metricValueItem: CanvasElementItem<TextConfig, TextData> = {
},
}),
prepareData: (ctx: DimensionContext, cfg: TextConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<TextConfig>) => {
const textConfig = elementOptions.config;
const data: TextData = {
text: cfg.text ? ctx.getText(cfg.text).value() : '',
align: cfg.align ?? Align.Center,
valign: cfg.valign ?? VAlign.Middle,
size: cfg.size,
text: textConfig?.text ? dimensionContext.getText(textConfig.text).value() : '',
align: textConfig?.align ?? Align.Center,
valign: textConfig?.valign ?? VAlign.Middle,
size: textConfig?.size,
};
if (cfg.color) {
data.color = ctx.getColor(cfg.color).value();
if (textConfig?.color) {
data.color = dimensionContext.getColor(textConfig.color).value();
}
data.links = getDataLinks(ctx, cfg, data.text);
data.links = getDataLinks(dimensionContext, elementOptions, data.text);
return data;
},

@ -9,7 +9,13 @@ import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimen
import { TextDimensionEditor } from 'app/features/dimensions/editors/TextDimensionEditor';
import { getDataLinks } from 'app/plugins/panel/canvas/utils';
import { CanvasElementItem, CanvasElementProps, defaultBgColor, defaultTextColor } from '../element';
import {
CanvasElementItem,
CanvasElementOptions,
CanvasElementProps,
defaultBgColor,
defaultTextColor,
} from '../element';
import { Align, TextConfig, TextData, VAlign } from '../types';
class RectangleDisplay extends PureComponent<CanvasElementProps<TextConfig, TextData>> {
@ -70,19 +76,21 @@ export const rectangleItem: CanvasElementItem<TextConfig, TextData> = {
}),
// Called when data changes
prepareData: (ctx: DimensionContext, cfg: TextConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<TextConfig>) => {
const textConfig = elementOptions.config;
const data: TextData = {
text: cfg.text ? ctx.getText(cfg.text).value() : '',
align: cfg.align ?? Align.Center,
valign: cfg.valign ?? VAlign.Middle,
size: cfg.size,
text: textConfig?.text ? dimensionContext.getText(textConfig.text).value() : '',
align: textConfig?.align ?? Align.Center,
valign: textConfig?.valign ?? VAlign.Middle,
size: textConfig?.size,
};
if (cfg.color) {
data.color = ctx.getColor(cfg.color).value();
if (textConfig?.color) {
data.color = dimensionContext.getColor(textConfig.color).value();
}
data.links = getDataLinks(ctx, cfg, data.text);
data.links = getDataLinks(dimensionContext, elementOptions, data.text);
return data;
},

@ -1,13 +1,14 @@
import { css } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { GrafanaTheme2, LinkModel } from '@grafana/data';
import { ColorDimensionConfig, ScalarDimensionConfig } from '@grafana/schema';
import config from 'app/core/config';
import { DimensionContext } from 'app/features/dimensions';
import { ColorDimensionEditor, ScalarDimensionEditor } from 'app/features/dimensions/editors';
import { getDataLinks } from 'app/plugins/panel/canvas/utils';
import { CanvasElementItem, CanvasElementProps } from '../../element';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps } from '../../element';
import { ServerDatabase } from './types/database';
import { ServerSingle } from './types/single';
@ -26,6 +27,7 @@ export interface ServerData {
statusColor?: string;
bulbColor?: string;
type: ServerType;
links?: LinkModel[];
}
enum ServerType {
@ -85,14 +87,20 @@ export const serverItem: CanvasElementItem<ServerConfig, ServerData> = {
}),
// Called when data changes
prepareData: (ctx: DimensionContext, cfg: ServerConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<ServerConfig>) => {
const serverConfig = elementOptions.config;
const data: ServerData = {
blinkRate: cfg?.blinkRate ? ctx.getScalar(cfg.blinkRate).value() : 0,
statusColor: cfg?.statusColor ? ctx.getColor(cfg.statusColor).value() : 'transparent',
bulbColor: cfg?.bulbColor ? ctx.getColor(cfg.bulbColor).value() : 'green',
type: cfg.type,
blinkRate: serverConfig?.blinkRate ? dimensionContext.getScalar(serverConfig.blinkRate).value() : 0,
statusColor: serverConfig?.statusColor
? dimensionContext.getColor(serverConfig.statusColor).value()
: 'transparent',
bulbColor: serverConfig?.bulbColor ? dimensionContext.getColor(serverConfig.bulbColor).value() : 'green',
type: serverConfig?.type ?? ServerType.Single,
};
data.links = getDataLinks(dimensionContext, elementOptions, data.statusColor);
return data;
},

@ -10,7 +10,7 @@ import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimen
import { TextDimensionEditor } from 'app/features/dimensions/editors/TextDimensionEditor';
import { getDataLinks } from '../../../plugins/panel/canvas/utils';
import { CanvasElementItem, CanvasElementProps, defaultThemeTextColor } from '../element';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps, defaultThemeTextColor } from '../element';
import { ElementState } from '../runtime/element';
import { Align, TextConfig, TextData, VAlign } from '../types';
@ -149,19 +149,21 @@ export const textItem: CanvasElementItem<TextConfig, TextData> = {
},
}),
prepareData: (ctx: DimensionContext, cfg: TextConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<TextConfig>) => {
const textConfig = elementOptions.config;
const data: TextData = {
text: cfg.text ? ctx.getText(cfg.text).value() : '',
align: cfg.align ?? Align.Center,
valign: cfg.valign ?? VAlign.Middle,
size: cfg.size,
text: textConfig?.text ? dimensionContext.getText(textConfig.text).value() : '',
align: textConfig?.align ?? Align.Center,
valign: textConfig?.valign ?? VAlign.Middle,
size: textConfig?.size,
};
if (cfg.color) {
data.color = ctx.getColor(cfg.color).value();
if (textConfig?.color) {
data.color = dimensionContext.getColor(textConfig.color).value();
}
data.links = getDataLinks(ctx, cfg, data.text);
data.links = getDataLinks(dimensionContext, elementOptions, data.text);
return data;
},

@ -1,16 +1,18 @@
import { css } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { GrafanaTheme2, LinkModel } from '@grafana/data';
import { ScalarDimensionConfig } from '@grafana/schema';
import { useStyles2 } from '@grafana/ui';
import { DimensionContext } from 'app/features/dimensions';
import { ScalarDimensionEditor } from 'app/features/dimensions/editors';
import { getDataLinks } from 'app/plugins/panel/canvas/utils';
import { CanvasElementItem, CanvasElementProps, defaultBgColor } from '../element';
import { CanvasElementItem, CanvasElementOptions, CanvasElementProps, defaultBgColor } from '../element';
interface WindTurbineData {
rpm?: number;
links?: LinkModel[];
}
interface WindTurbineConfig {
@ -92,11 +94,15 @@ export const windTurbineItem: CanvasElementItem = {
}),
// Called when data changes
prepareData: (ctx: DimensionContext, cfg: WindTurbineConfig) => {
prepareData: (dimensionContext: DimensionContext, elementOptions: CanvasElementOptions<WindTurbineConfig>) => {
const windTurbineConfig = elementOptions.config;
const data: WindTurbineData = {
rpm: cfg?.rpm ? ctx.getScalar(cfg.rpm).value() : 0,
rpm: windTurbineConfig?.rpm ? dimensionContext.getScalar(windTurbineConfig.rpm).value() : 0,
};
data.links = getDataLinks(dimensionContext, elementOptions, `${data.rpm}`);
return data;
},

@ -296,7 +296,7 @@ export class ElementState implements LayerElement {
updateData(ctx: DimensionContext) {
if (this.item.prepareData) {
this.data = this.item.prepareData(ctx, this.options.config);
this.data = this.item.prepareData(ctx, this.options);
this.revId++; // rerender
}

@ -9,7 +9,6 @@ import {
CanvasElementItem,
canvasElementRegistry,
CanvasElementOptions,
TextConfig,
CanvasConnection,
} from 'app/features/canvas';
import { notFoundItem } from 'app/features/canvas/elements/notFound';
@ -107,20 +106,18 @@ export function onAddItem(sel: SelectableValue<string>, rootLayer: FrameState |
}
}
export function getDataLinks(ctx: DimensionContext, cfg: TextConfig, textData: string | undefined): LinkModel[] {
const panelData = ctx.getPanelData();
const frames = panelData?.series;
const links: Array<LinkModel<Field>> = [];
const linkLookup = new Set<string>();
frames?.forEach((frame) => {
const visibleFields = frame.fields.filter((field) => !Boolean(field.config.custom?.hideFrom?.tooltip));
if (cfg.text?.field && visibleFields.some((f) => getFieldDisplayName(f, frame) === cfg.text?.field)) {
const field = visibleFields.filter((field) => getFieldDisplayName(field, frame) === cfg.text?.field)[0];
/*
* Provided a given field add any matching data links
* Mutates the links object in place which is then returned by the `getDataLinks` function downstream
*/
const addDataLinkForField = (
field: Field<unknown>,
data: string | undefined,
linkLookup: Set<string>,
links: Array<LinkModel<Field>>
): void => {
if (field?.getLinks) {
const disp = field.display ? field.display(textData) : { text: `${textData}`, numeric: +textData! };
const disp = field.display ? field.display(data) : { text: `${data}`, numeric: +data! };
field.getLinks({ calculatedValue: disp }).forEach((link) => {
const key = `${link.title}/${link.href}`;
if (!linkLookup.has(key)) {
@ -129,6 +126,135 @@ export function getDataLinks(ctx: DimensionContext, cfg: TextConfig, textData: s
}
});
}
};
// TODO: This could be refactored a fair amount, ideally the element specific config code should be owned by each element and not in this shared util file
export function getDataLinks(
dimensionContext: DimensionContext,
elementOptions: CanvasElementOptions,
data: string | undefined
): LinkModel[] {
const panelData = dimensionContext.getPanelData();
const frames = panelData?.series;
const links: Array<LinkModel<Field>> = [];
const linkLookup = new Set<string>();
const elementConfig = elementOptions.config;
frames?.forEach((frame) => {
const visibleFields = frame.fields.filter((field) => !Boolean(field.config.custom?.hideFrom?.tooltip));
// Text config
const isTextTiedToFieldData =
elementConfig.text?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementConfig.text?.field);
const isTextColorTiedToFieldData =
elementConfig.color?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementConfig.color?.field);
// General element config
const isElementBackgroundColorTiedToFieldData =
elementOptions?.background?.color?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementOptions?.background?.color?.field);
const isElementBackgroundImageTiedToFieldData =
elementOptions?.background?.image?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementOptions?.background?.image?.field);
const isElementBorderColorTiedToFieldData =
elementOptions?.border?.color?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementOptions?.border?.color?.field);
// Icon config
const isIconSVGTiedToFieldData =
elementConfig.path?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementConfig.path?.field);
const isIconColorTiedToFieldData =
elementConfig.fill?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementConfig.fill?.field);
// Wind turbine config (maybe remove / not support this?)
const isWindTurbineRPMTiedToFieldData =
elementConfig.rpm?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementConfig.rpm?.field);
// Server config
const isServerBlinkRateTiedToFieldData =
elementConfig.blinkRate?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementConfig.blinkRate?.field);
const isServerStatusColorTiedToFieldData =
elementConfig.statusColor?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementConfig.statusColor?.field);
const isServerBulbColorTiedToFieldData =
elementConfig.bulbColor?.field &&
visibleFields.some((field) => getFieldDisplayName(field, frame) === elementConfig.bulbColor?.field);
if (isTextTiedToFieldData) {
const field = visibleFields.filter((field) => getFieldDisplayName(field, frame) === elementConfig.text?.field)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isTextColorTiedToFieldData) {
const field = visibleFields.filter(
(field) => getFieldDisplayName(field, frame) === elementConfig.color?.field
)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isElementBackgroundColorTiedToFieldData) {
const field = visibleFields.filter(
(field) => getFieldDisplayName(field, frame) === elementOptions?.background?.color?.field
)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isElementBackgroundImageTiedToFieldData) {
const field = visibleFields.filter(
(field) => getFieldDisplayName(field, frame) === elementOptions?.background?.image?.field
)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isElementBorderColorTiedToFieldData) {
const field = visibleFields.filter(
(field) => getFieldDisplayName(field, frame) === elementOptions?.border?.color?.field
)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isIconSVGTiedToFieldData) {
const field = visibleFields.filter((field) => getFieldDisplayName(field, frame) === elementConfig.path?.field)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isIconColorTiedToFieldData) {
const field = visibleFields.filter((field) => getFieldDisplayName(field, frame) === elementConfig.fill?.field)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isWindTurbineRPMTiedToFieldData) {
const field = visibleFields.filter((field) => getFieldDisplayName(field, frame) === elementConfig.rpm?.field)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isServerBlinkRateTiedToFieldData) {
const field = visibleFields.filter(
(field) => getFieldDisplayName(field, frame) === elementConfig.blinkRate?.field
)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isServerStatusColorTiedToFieldData) {
const field = visibleFields.filter(
(field) => getFieldDisplayName(field, frame) === elementConfig.statusColor?.field
)[0];
addDataLinkForField(field, data, linkLookup, links);
}
if (isServerBulbColorTiedToFieldData) {
const field = visibleFields.filter(
(field) => getFieldDisplayName(field, frame) === elementConfig.bulbColor?.field
)[0];
addDataLinkForField(field, data, linkLookup, links);
}
});

Loading…
Cancel
Save