mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
Canvas: Element data links refactor (#90636)
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
parent
d3061ab61a
commit
0a870e6a88
@ -190,12 +190,12 @@ You can configure a canvas data link to open with a single click on the element.
|
||||
1. Click the element to which you want to add the data link.
|
||||
1. In either the inline editor or panel editor, expand the **Selected element** editor.
|
||||
1. Scroll down to the **Data links** section and expand it.
|
||||
1. Toggle the **One-click** switch in the element's data links section.
|
||||
1. In the **One-click** section, choose **Link**.
|
||||
1. Disable inline editing.
|
||||
|
||||
The first data link in the list will be configured as your one-click data link. If you want to change the one-click data link, simply drag the desired data link to the top of the list.
|
||||
|
||||
{{< video-embed src="/media/docs/grafana/panels-visualizations/canvas-one-click-data-link.mp4" >}}
|
||||
{{< video-embed src="/media/docs/grafana/panels-visualizations/canvas-one-click-datalink-.mp4" >}}
|
||||
|
||||
## Panel options
|
||||
|
||||
|
@ -479,7 +479,6 @@ export const getLinksSupplier =
|
||||
href,
|
||||
title: replaceVariables(link.title || '', dataLinkScopedVars),
|
||||
target: link.targetBlank ? '_blank' : undefined,
|
||||
sortIndex: link.sortIndex,
|
||||
onClick: (evt: MouseEvent, origin: Field) => {
|
||||
link.onClick!({
|
||||
origin: origin ?? field,
|
||||
@ -495,7 +494,6 @@ export const getLinksSupplier =
|
||||
title: replaceVariables(link.title || '', dataLinkScopedVars),
|
||||
target: link.targetBlank ? '_blank' : undefined,
|
||||
origin: field,
|
||||
sortIndex: link.sortIndex,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -793,6 +793,7 @@ export {
|
||||
VariableOrigin,
|
||||
type VariableSuggestion,
|
||||
VariableSuggestionsScope,
|
||||
OneClickMode,
|
||||
} from './types/dataLink';
|
||||
export { DataFrameType } from './types/dataFrameTypes';
|
||||
export {
|
||||
|
@ -99,7 +99,6 @@ export interface LinkModel<T = any> {
|
||||
|
||||
// When a click callback exists, this is passed the raw mouse|react event
|
||||
onClick?: (e: any, origin?: any) => void;
|
||||
sortIndex?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,3 +129,8 @@ export interface VariableSuggestion {
|
||||
export enum VariableSuggestionsScope {
|
||||
Values = 'values',
|
||||
}
|
||||
|
||||
export enum OneClickMode {
|
||||
Link = 'link',
|
||||
Off = 'off',
|
||||
}
|
||||
|
@ -104,7 +104,6 @@ export interface CanvasElementOptions {
|
||||
connections?: Array<CanvasConnection>;
|
||||
constraint?: Constraint;
|
||||
name: string;
|
||||
oneClickLinks?: boolean;
|
||||
placement?: Placement;
|
||||
type: string;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ interface DataLinksInlineEditorProps {
|
||||
onChange: (links: DataLink[]) => void;
|
||||
getSuggestions: () => VariableSuggestion[];
|
||||
data: DataFrame[];
|
||||
oneClickEnabled?: boolean;
|
||||
showOneClick?: boolean;
|
||||
}
|
||||
|
||||
export const DataLinksInlineEditor = ({
|
||||
@ -25,13 +25,12 @@ export const DataLinksInlineEditor = ({
|
||||
onChange,
|
||||
getSuggestions,
|
||||
data,
|
||||
oneClickEnabled = false,
|
||||
showOneClick = false,
|
||||
}: DataLinksInlineEditorProps) => {
|
||||
const [editIndex, setEditIndex] = useState<number | null>(null);
|
||||
const [isNew, setIsNew] = useState(false);
|
||||
|
||||
const [linksSafe, setLinksSafe] = useState<DataLink[]>([]);
|
||||
links?.sort((a, b) => (a.sortIndex ?? 0) - (b.sortIndex ?? 0));
|
||||
|
||||
useEffect(() => {
|
||||
setLinksSafe(links ?? []);
|
||||
@ -81,24 +80,20 @@ export const DataLinksInlineEditor = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const copy = [...linksSafe];
|
||||
const link = copy[result.source.index];
|
||||
link.sortIndex = result.destination.index;
|
||||
const update = cloneDeep(linksSafe);
|
||||
const link = update[result.source.index];
|
||||
|
||||
const swapLink = copy[result.destination.index];
|
||||
swapLink.sortIndex = result.source.index;
|
||||
update.splice(result.source.index, 1);
|
||||
update.splice(result.destination.index, 0, link);
|
||||
|
||||
copy.splice(result.source.index, 1);
|
||||
copy.splice(result.destination.index, 0, link);
|
||||
|
||||
setLinksSafe(copy);
|
||||
onChange(linksSafe);
|
||||
setLinksSafe(update);
|
||||
onChange(update);
|
||||
};
|
||||
|
||||
const renderFirstLink = (linkJSX: ReactNode) => {
|
||||
if (oneClickEnabled) {
|
||||
const renderFirstLink = (linkJSX: ReactNode, key: string) => {
|
||||
if (showOneClick) {
|
||||
return (
|
||||
<div className={styles.oneClickOverlay}>
|
||||
<div className={styles.oneClickOverlay} key={key}>
|
||||
<span className={styles.oneClickSpan}>One-click</span>
|
||||
{linkJSX}
|
||||
</div>
|
||||
@ -130,7 +125,7 @@ export const DataLinksInlineEditor = ({
|
||||
);
|
||||
|
||||
if (idx === 0) {
|
||||
return renderFirstLink(linkJSX);
|
||||
return renderFirstLink(linkJSX, key);
|
||||
}
|
||||
|
||||
return linkJSX;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
import { DataLink, RegistryItem } from '@grafana/data';
|
||||
import { DataLink, RegistryItem, OneClickMode } from '@grafana/data';
|
||||
import { PanelOptionsSupplier } from '@grafana/data/src/panel/PanelPlugin';
|
||||
import { ColorDimensionConfig, ScaleDimensionConfig } from '@grafana/schema';
|
||||
import { config } from 'app/core/config';
|
||||
@ -33,7 +33,7 @@ export interface CanvasElementOptions<TConfig = any> {
|
||||
border?: LineConfig;
|
||||
connections?: CanvasConnection[];
|
||||
links?: DataLink[];
|
||||
oneClickLinks?: boolean;
|
||||
oneClickMode?: OneClickMode;
|
||||
}
|
||||
|
||||
// Unit is percentage from the middle of the element
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { config } from 'app/core/config';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimensionEditor';
|
||||
@ -99,6 +99,7 @@ export const cloudItem: CanvasElementItem = {
|
||||
left: options?.placement?.left,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { ScalarDimensionConfig } from '@grafana/schema';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
@ -94,6 +94,8 @@ export const droneFrontItem: CanvasElementItem = {
|
||||
left: options?.placement?.left,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
// Called when data changes
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { ScalarDimensionConfig } from '@grafana/schema';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
@ -93,6 +93,8 @@ export const droneSideItem: CanvasElementItem = {
|
||||
left: options?.placement?.left,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
// Called when data changes
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { ScalarDimensionConfig } from '@grafana/schema';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
@ -98,6 +98,8 @@ export const droneTopItem: CanvasElementItem = {
|
||||
fixed: 'transparent',
|
||||
},
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
// Called when data changes
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { config } from 'app/core/config';
|
||||
import { DimensionContext } from 'app/features/dimensions/context';
|
||||
import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimensionEditor';
|
||||
@ -106,6 +106,7 @@ export const ellipseItem: CanvasElementItem<CanvasElementConfig, CanvasElementDa
|
||||
left: options?.placement?.left,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
||||
import { isString } from 'lodash';
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
import { LinkModel } from '@grafana/data';
|
||||
import { LinkModel, OneClickMode } from '@grafana/data';
|
||||
import { ColorDimensionConfig, ResourceDimensionConfig, ResourceDimensionMode } from '@grafana/schema';
|
||||
import { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG';
|
||||
import { getPublicOrAbsoluteUrl } from 'app/features/dimensions';
|
||||
@ -80,6 +80,7 @@ export const iconItem: CanvasElementItem<IconConfig, IconData> = {
|
||||
left: options?.placement?.left ?? 100,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -3,7 +3,13 @@ import { useCallback } from 'react';
|
||||
import { useObservable } from 'react-use';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { DataFrame, FieldNamePickerConfigSettings, GrafanaTheme2, StandardEditorsRegistryItem } from '@grafana/data';
|
||||
import {
|
||||
DataFrame,
|
||||
FieldNamePickerConfigSettings,
|
||||
GrafanaTheme2,
|
||||
OneClickMode,
|
||||
StandardEditorsRegistryItem,
|
||||
} from '@grafana/data';
|
||||
import { TextDimensionMode } from '@grafana/schema';
|
||||
import { usePanelContext, useStyles2 } from '@grafana/ui';
|
||||
import { FieldNamePicker } from '@grafana/ui/src/components/MatchersUI/FieldNamePicker';
|
||||
@ -175,6 +181,7 @@ export const metricValueItem: CanvasElementItem<TextConfig, TextData> = {
|
||||
left: options?.placement?.left ?? 100,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { config } from 'app/core/config';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimensionEditor';
|
||||
@ -99,6 +99,7 @@ export const parallelogramItem: CanvasElementItem = {
|
||||
left: options?.placement?.left,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { stylesFactory } from '@grafana/ui';
|
||||
import { config } from 'app/core/config';
|
||||
import { DimensionContext } from 'app/features/dimensions/context';
|
||||
@ -72,6 +72,7 @@ export const rectangleItem: CanvasElementItem<TextConfig, TextData> = {
|
||||
fixed: defaultBgColor,
|
||||
},
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2, LinkModel } from '@grafana/data';
|
||||
import { GrafanaTheme2, LinkModel, OneClickMode } from '@grafana/data';
|
||||
import { ColorDimensionConfig, ScalarDimensionConfig } from '@grafana/schema';
|
||||
import config from 'app/core/config';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
@ -83,6 +83,7 @@ export const serverItem: CanvasElementItem<ServerConfig, ServerData> = {
|
||||
config: {
|
||||
type: ServerType.Single,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
import { useObservable } from 'react-use';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { DataFrame, GrafanaTheme2 } from '@grafana/data';
|
||||
import { DataFrame, GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { Input, usePanelContext, useStyles2 } from '@grafana/ui';
|
||||
import { DimensionContext } from 'app/features/dimensions/context';
|
||||
import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimensionEditor';
|
||||
@ -148,6 +148,7 @@ export const textItem: CanvasElementItem<TextConfig, TextData> = {
|
||||
left: options?.placement?.left,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, OneClickMode } from '@grafana/data';
|
||||
import { config } from 'app/core/config';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
import { ColorDimensionEditor } from 'app/features/dimensions/editors/ColorDimensionEditor';
|
||||
@ -100,6 +100,7 @@ export const triangleItem: CanvasElementItem = {
|
||||
left: options?.placement?.left,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2, LinkModel } from '@grafana/data';
|
||||
import { GrafanaTheme2, LinkModel, OneClickMode } from '@grafana/data';
|
||||
import { ScalarDimensionConfig } from '@grafana/schema';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
@ -90,6 +90,7 @@ export const windTurbineItem: CanvasElementItem = {
|
||||
left: options?.placement?.left,
|
||||
rotation: options?.placement?.rotation ?? 0,
|
||||
},
|
||||
oneClickMode: options?.oneClickMode ?? OneClickMode.Off,
|
||||
links: options?.links ?? [],
|
||||
}),
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { CSSProperties } from 'react';
|
||||
import * as React from 'react';
|
||||
import { CSSProperties } from 'react';
|
||||
import { OnDrag, OnResize, OnRotate } from 'react-moveable/declaration/types';
|
||||
|
||||
import { FieldType, getLinksSupplier, LinkModel, ValueLinkConfig } from '@grafana/data';
|
||||
import { FieldType, getLinksSupplier, LinkModel, OneClickMode, ValueLinkConfig } from '@grafana/data';
|
||||
import { LayerElement } from 'app/core/components/Layers/types';
|
||||
import { notFoundItem } from 'app/features/canvas/elements/notFound';
|
||||
import { DimensionContext } from 'app/features/dimensions';
|
||||
@ -62,6 +62,7 @@ export class ElementState implements LayerElement {
|
||||
options.placement = options.placement ?? { width: 100, height: 100, top: 0, left: 0, rotation: 0 };
|
||||
options.background = options.background ?? { color: { fixed: 'transparent' } };
|
||||
options.border = options.border ?? { color: { fixed: 'dark-green' } };
|
||||
options.oneClickMode = options.oneClickMode ?? OneClickMode.Off;
|
||||
const scene = this.getScene();
|
||||
if (!options.name) {
|
||||
const newName = scene?.getNextElementName();
|
||||
@ -584,13 +585,17 @@ export class ElementState implements LayerElement {
|
||||
handleMouseEnter = (event: React.MouseEvent, isSelected: boolean | undefined) => {
|
||||
const scene = this.getScene();
|
||||
|
||||
if (!scene?.isEditingEnabled && !scene?.tooltip?.isOpen && !this.options.oneClickLinks) {
|
||||
const shouldHandleTooltip =
|
||||
!scene?.isEditingEnabled && !scene?.tooltip?.isOpen && this.options.oneClickMode === OneClickMode.Off;
|
||||
if (shouldHandleTooltip) {
|
||||
this.handleTooltip(event);
|
||||
} else if (!isSelected) {
|
||||
scene?.connections.handleMouseEnter(event);
|
||||
}
|
||||
|
||||
if (this.options.oneClickLinks && this.div && this.options.links && this.options.links.length > 0) {
|
||||
const shouldHandleOneClickLink =
|
||||
this.options.oneClickMode === OneClickMode.Link && this.options.links && this.options.links.length > 0;
|
||||
if (shouldHandleOneClickLink && this.div) {
|
||||
const primaryDataLink = this.getPrimaryDataLink();
|
||||
if (primaryDataLink) {
|
||||
this.div.style.cursor = 'pointer';
|
||||
@ -602,8 +607,7 @@ export class ElementState implements LayerElement {
|
||||
getPrimaryDataLink = () => {
|
||||
if (this.getLinks) {
|
||||
const links = this.getLinks({ valueRowIndex: getRowIndex(this.data.field, this.getScene()!) });
|
||||
const primaryDataLink = links.find((link: LinkModel) => link.sortIndex === 0);
|
||||
return primaryDataLink ?? links[0];
|
||||
return links[0];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@ -623,11 +627,11 @@ export class ElementState implements LayerElement {
|
||||
|
||||
handleMouseLeave = (event: React.MouseEvent) => {
|
||||
const scene = this.getScene();
|
||||
if (scene?.tooltipCallback && !scene?.tooltip?.isOpen && !this.options.oneClickLinks) {
|
||||
if (scene?.tooltipCallback && !scene?.tooltip?.isOpen && this.options.oneClickMode === OneClickMode.Off) {
|
||||
scene.tooltipCallback(undefined);
|
||||
}
|
||||
|
||||
if (this.options.oneClickLinks && this.div) {
|
||||
if (this.options.oneClickMode !== OneClickMode.Off && this.div) {
|
||||
this.div.style.cursor = 'auto';
|
||||
this.div.title = '';
|
||||
}
|
||||
@ -635,7 +639,7 @@ export class ElementState implements LayerElement {
|
||||
|
||||
onElementClick = (event: React.MouseEvent) => {
|
||||
// If one-click access is enabled, open the primary link
|
||||
if (this.options.oneClickLinks) {
|
||||
if (this.options.oneClickMode === OneClickMode.Link) {
|
||||
let primaryDataLink = this.getPrimaryDataLink();
|
||||
if (primaryDataLink) {
|
||||
window.open(primaryDataLink.href, primaryDataLink.target);
|
||||
|
@ -83,9 +83,6 @@ export const CanvasTooltip = ({ scene }: Props) => {
|
||||
});
|
||||
}
|
||||
|
||||
// sort element data links
|
||||
links.sort((a, b) => (a.sortIndex ?? 0) - (b.sortIndex ?? 0));
|
||||
|
||||
return (
|
||||
<>
|
||||
{scene.tooltip?.element && scene.tooltip.anchorPoint && (
|
||||
|
@ -1,16 +1,11 @@
|
||||
import { StandardEditorProps, DataLink, VariableSuggestionsScope } from '@grafana/data';
|
||||
import { StandardEditorProps, DataLink, VariableSuggestionsScope, OneClickMode } from '@grafana/data';
|
||||
import { DataLinksInlineEditor } from '@grafana/ui';
|
||||
|
||||
import { CanvasElementOptions } from '../../panelcfg.gen';
|
||||
import { CanvasElementOptions } from 'app/features/canvas/element';
|
||||
|
||||
type Props = StandardEditorProps<DataLink[], CanvasElementOptions>;
|
||||
|
||||
export function DataLinksEditor({ value, onChange, item, context }: Props) {
|
||||
if (!value) {
|
||||
value = [];
|
||||
}
|
||||
|
||||
const settings = item.settings;
|
||||
const oneClickMode = item.settings?.oneClickMode;
|
||||
|
||||
return (
|
||||
<DataLinksInlineEditor
|
||||
@ -18,7 +13,7 @@ export function DataLinksEditor({ value, onChange, item, context }: Props) {
|
||||
onChange={onChange}
|
||||
getSuggestions={() => (context.getSuggestions ? context.getSuggestions(VariableSuggestionsScope.Values) : [])}
|
||||
data={[]}
|
||||
oneClickEnabled={settings?.oneClickLinks}
|
||||
showOneClick={oneClickMode === OneClickMode.Link}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { get as lodashGet } from 'lodash';
|
||||
import { capitalize, get as lodashGet } from 'lodash';
|
||||
|
||||
import { OneClickMode } from '@grafana/data';
|
||||
import { NestedPanelOptions, NestedValueAccess } from '@grafana/data/src/utils/OptionsUIBuilders';
|
||||
import { CanvasElementOptions } from 'app/features/canvas/element';
|
||||
import {
|
||||
@ -119,6 +120,20 @@ export function getElementEditor(opts: CanvasEditorOptions): NestedPanelOptions<
|
||||
optionBuilder.addBorder(builder, ctx);
|
||||
}
|
||||
|
||||
builder.addRadio({
|
||||
category: ['Data links'],
|
||||
path: 'oneClickMode',
|
||||
name: 'One-click',
|
||||
description: 'When enabled, a single click opens the first link',
|
||||
settings: {
|
||||
options: [
|
||||
{ value: OneClickMode.Off, label: capitalize(OneClickMode.Off) },
|
||||
{ value: OneClickMode.Link, label: capitalize(OneClickMode.Link) },
|
||||
],
|
||||
},
|
||||
defaultValue: OneClickMode.Off,
|
||||
});
|
||||
|
||||
optionBuilder.addDataLinks(builder, ctx);
|
||||
},
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { capitalize } from 'lodash';
|
||||
|
||||
import { FieldType, standardEditorsRegistry } from '@grafana/data';
|
||||
import { FieldType } from '@grafana/data';
|
||||
import { PanelOptionsSupplier } from '@grafana/data/src/panel/PanelPlugin';
|
||||
import { ConnectionDirection } from 'app/features/canvas/element';
|
||||
import { SVGElements } from 'app/features/canvas/runtime/element';
|
||||
@ -209,23 +209,13 @@ export const optionBuilder: OptionSuppliers = {
|
||||
},
|
||||
|
||||
addDataLinks: (builder, context) => {
|
||||
const category = ['Data links'];
|
||||
builder
|
||||
.addCustomEditor({
|
||||
category,
|
||||
id: 'enableOneClick',
|
||||
path: 'oneClickLinks',
|
||||
name: 'One-click',
|
||||
description: 'When enabled, the top link in the list below works with a single click',
|
||||
editor: standardEditorsRegistry.get('boolean').editor,
|
||||
})
|
||||
.addCustomEditor({
|
||||
category,
|
||||
id: 'dataLinks',
|
||||
path: 'links',
|
||||
name: '',
|
||||
editor: DataLinksEditor,
|
||||
settings: context.options,
|
||||
});
|
||||
builder.addCustomEditor({
|
||||
category: ['Data links'],
|
||||
id: 'dataLinks',
|
||||
path: 'links',
|
||||
name: 'Links',
|
||||
editor: DataLinksEditor,
|
||||
settings: context.options,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { DataLink, DynamicConfigValue, FieldMatcherID, PanelModel } from '@grafana/data';
|
||||
import { DataLink, DynamicConfigValue, FieldMatcherID, PanelModel, OneClickMode } from '@grafana/data';
|
||||
import { CanvasElementOptions } from 'app/features/canvas/element';
|
||||
|
||||
import { Options } from './panelcfg.gen';
|
||||
@ -43,7 +43,8 @@ export const canvasMigrationHandler = (panel: PanelModel): Partial<Options> => {
|
||||
}
|
||||
}
|
||||
|
||||
if (parseFloat(pluginVersion) <= 11.2) {
|
||||
if (parseFloat(pluginVersion) <= 11.3) {
|
||||
// migrate links from field name overrides to elements
|
||||
for (let idx = 0; idx < panel.fieldConfig.overrides.length; idx++) {
|
||||
const override = panel.fieldConfig.overrides[idx];
|
||||
|
||||
@ -66,6 +67,17 @@ export const canvasMigrationHandler = (panel: PanelModel): Partial<Options> => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// migrate oneClickLinks to oneClickMode
|
||||
const root = panel.options?.root;
|
||||
if (root?.elements) {
|
||||
for (const element of root.elements) {
|
||||
if (element.oneClickLinks) {
|
||||
element.oneClickMode = OneClickMode.Link;
|
||||
delete element.oneClickLinks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return panel.options;
|
||||
|
@ -87,7 +87,6 @@ composableKinds: PanelCfg: {
|
||||
background?: BackgroundConfig
|
||||
border?: LineConfig
|
||||
connections?: [...CanvasConnection]
|
||||
oneClickLinks?: bool
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
Options: {
|
||||
|
@ -102,7 +102,6 @@ export interface CanvasElementOptions {
|
||||
connections?: Array<CanvasConnection>;
|
||||
constraint?: Constraint;
|
||||
name: string;
|
||||
oneClickLinks?: boolean;
|
||||
placement?: Placement;
|
||||
type: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user