mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Geomap: Display legend (#46886)
* Display legend for fixed colors and field; Hide tooltip on base layer;
This commit is contained in:
parent
b52794601d
commit
118b87ee8f
@ -46,6 +46,7 @@ type Props = PanelProps<GeomapPanelOptions>;
|
||||
interface State extends OverlayProps {
|
||||
ttip?: GeomapHoverPayload;
|
||||
ttipOpen: boolean;
|
||||
legends: ReactNode[];
|
||||
}
|
||||
|
||||
export interface GeomapLayerActions {
|
||||
@ -82,7 +83,7 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { ttipOpen: false };
|
||||
this.state = { ttipOpen: false, legends: [] };
|
||||
this.subs.add(
|
||||
this.props.eventBus.subscribe(PanelEditExitedEvent, (evt) => {
|
||||
if (this.mapDiv && this.props.id === evt.payload) {
|
||||
@ -114,7 +115,7 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
return true; // always?
|
||||
}
|
||||
|
||||
/** This funciton will actually update the JSON model */
|
||||
/** This function will actually update the JSON model */
|
||||
private doOptionsUpdate(selected: number) {
|
||||
const { options, onOptionsChange } = this.props;
|
||||
const layers = this.layers;
|
||||
@ -124,7 +125,7 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
layers: layers.slice(1).map((v) => v.options),
|
||||
});
|
||||
|
||||
// Notify the the panel editor
|
||||
// Notify the panel editor
|
||||
if (this.panelContext.onInstanceStateChange) {
|
||||
this.panelContext.onInstanceStateChange({
|
||||
map: this.map,
|
||||
@ -133,6 +134,8 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
actions: this.actions,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ legends: this.getLegends() });
|
||||
}
|
||||
|
||||
getNextLayerName = () => {
|
||||
@ -308,6 +311,8 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
actions: this.actions,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ legends: this.getLegends() });
|
||||
};
|
||||
|
||||
clearTooltip = () => {
|
||||
@ -447,6 +452,9 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Just to trigger a state update
|
||||
this.setState({ legends: [] });
|
||||
|
||||
this.layers = layers;
|
||||
this.doOptionsUpdate(layerIndex);
|
||||
return true;
|
||||
@ -481,6 +489,7 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
if (!options.name) {
|
||||
options.name = this.getNextLayerName();
|
||||
}
|
||||
|
||||
const UID = options.name;
|
||||
const state: MapLayerState<any> = {
|
||||
// UID, // unique name when added to the map (it may change and will need special handling)
|
||||
@ -496,6 +505,7 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
this.updateLayer(UID, cfg);
|
||||
},
|
||||
};
|
||||
|
||||
this.byName.set(UID, state);
|
||||
(state.layer as any).__state = state;
|
||||
return state;
|
||||
@ -597,15 +607,26 @@ export class GeomapPanel extends Component<Props, State> {
|
||||
this.setState({ topRight });
|
||||
}
|
||||
|
||||
getLegends() {
|
||||
const legends: ReactNode[] = [];
|
||||
for (const state of this.layers) {
|
||||
if (state.handler.legend) {
|
||||
legends.push(<div key={state.options.name}>{state.handler.legend}</div>);
|
||||
}
|
||||
}
|
||||
|
||||
return legends;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { ttip, ttipOpen, topRight, bottomLeft } = this.state;
|
||||
const { ttip, ttipOpen, topRight, legends } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Global styles={this.globalCSS} />
|
||||
<div className={this.style.wrap} onMouseLeave={this.clearTooltip}>
|
||||
<div className={this.style.map} ref={this.initMapRef}></div>
|
||||
<GeomapOverlay bottomLeft={bottomLeft} topRight={topRight} />
|
||||
<GeomapOverlay bottomLeft={legends} topRight={topRight} />
|
||||
</div>
|
||||
<GeomapTooltip ttip={ttip} isOpen={ttipOpen} onClose={this.tooltipPopupClosed} />
|
||||
</>
|
||||
|
@ -4,7 +4,7 @@ import { NestedPanelOptions, NestedValueAccess } from '@grafana/data/src/utils/O
|
||||
import { defaultMarkersConfig } from '../layers/data/markersLayer';
|
||||
import { hasAlphaPanels } from 'app/core/config';
|
||||
import { MapLayerState } from '../types';
|
||||
import { get as lodashGet } from 'lodash';
|
||||
import { get as lodashGet, isEqual } from 'lodash';
|
||||
import { setOptionImmutably } from 'app/features/dashboard/components/PanelEditor/utils';
|
||||
import { addLocationFields } from 'app/features/geo/editor/locationEditor';
|
||||
|
||||
@ -93,12 +93,14 @@ export function getLayerEditor(opts: LayerEditorOptions): NestedPanelOptions<Map
|
||||
// TODO -- add opacity check
|
||||
}
|
||||
|
||||
builder.addBooleanSwitch({
|
||||
path: 'tooltip',
|
||||
name: 'Display tooltip',
|
||||
description: 'Show the tooltip for layer',
|
||||
defaultValue: true,
|
||||
});
|
||||
if (!isEqual(opts.category, ['Base layer'])) {
|
||||
builder.addBooleanSwitch({
|
||||
path: 'tooltip',
|
||||
name: 'Display tooltip',
|
||||
description: 'Show the tooltip for layer',
|
||||
defaultValue: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -5,28 +5,55 @@ import { css } from '@emotion/css';
|
||||
import { config } from 'app/core/config';
|
||||
import { DimensionSupplier } from 'app/features/dimensions';
|
||||
import { getThresholdItems } from 'app/plugins/panel/state-timeline/utils';
|
||||
import { getMinMaxAndDelta } from '../../../../../../../packages/grafana-data/src/field/scale';
|
||||
import { getMinMaxAndDelta } from '@grafana/data/src/field/scale';
|
||||
import SVG from 'react-inlinesvg';
|
||||
import { StyleConfigState } from '../../style/types';
|
||||
|
||||
export interface MarkersLegendProps {
|
||||
color?: DimensionSupplier<string>;
|
||||
size?: DimensionSupplier<number>;
|
||||
layerName?: string;
|
||||
styleConfig?: StyleConfigState;
|
||||
}
|
||||
|
||||
export function MarkersLegend(props: MarkersLegendProps) {
|
||||
const { color } = props;
|
||||
const { layerName, styleConfig } = props;
|
||||
const theme = useTheme2();
|
||||
const style = getStyles(theme);
|
||||
|
||||
if (!color || (!color.field && color.fixed)) {
|
||||
if (!styleConfig) {
|
||||
return <></>;
|
||||
}
|
||||
const { color, opacity} = styleConfig?.base ?? {};
|
||||
const symbol = styleConfig?.config.symbol?.fixed;
|
||||
|
||||
const colorField = styleConfig.dims?.color?.field;
|
||||
|
||||
if (color && symbol && !colorField) {
|
||||
return (
|
||||
<div className={style.infoWrap}>
|
||||
<div className={style.fixedColorContainer}>
|
||||
<SVG
|
||||
src={`public/${symbol}`}
|
||||
className={style.legendSymbol}
|
||||
title={'Symbol'}
|
||||
style={{ fill: color, opacity: opacity }}
|
||||
/>
|
||||
<span>{layerName}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!colorField) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const style = getStyles(theme);
|
||||
const fmt = (v: any) => `${formattedValueToString(color.field!.display!(v))}`;
|
||||
const colorMode = getFieldColorModeForField(color!.field!);
|
||||
const fmt = (v: any) => `${formattedValueToString(colorField.display!(v))}`;
|
||||
const colorMode = getFieldColorModeForField(colorField);
|
||||
|
||||
if (colorMode.isContinuous && colorMode.getColors) {
|
||||
const colors = colorMode.getColors(config.theme2);
|
||||
const colorRange = getMinMaxAndDelta(color.field!);
|
||||
const colorRange = getMinMaxAndDelta(colorField);
|
||||
// TODO: explore showing mean on the gradiant scale
|
||||
// const stats = reduceField({
|
||||
// field: color.field!,
|
||||
@ -40,7 +67,7 @@ export function MarkersLegend(props: MarkersLegendProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Label>{color?.field?.name}</Label>
|
||||
<Label>{colorField?.name}</Label>
|
||||
<div
|
||||
className={style.gradientContainer}
|
||||
style={{ backgroundImage: `linear-gradient(to right, ${colors.map((c) => c).join(', ')}` }}
|
||||
@ -52,12 +79,12 @@ export function MarkersLegend(props: MarkersLegendProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const thresholds = color.field?.config?.thresholds;
|
||||
const thresholds = colorField?.config?.thresholds;
|
||||
if (!thresholds || thresholds.steps.length < 2) {
|
||||
return <div></div>; // don't show anything in the legend
|
||||
}
|
||||
|
||||
const items = getThresholdItems(color.field!.config, config.theme2);
|
||||
const items = getThresholdItems(colorField!.config, config.theme2);
|
||||
return (
|
||||
<div className={style.infoWrap}>
|
||||
<div className={style.legend}>
|
||||
@ -95,6 +122,16 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => ({
|
||||
legendItem: css`
|
||||
white-space: nowrap;
|
||||
`,
|
||||
fixedColorContainer: css`
|
||||
min-width: 80px;
|
||||
font-size: ${theme.typography.bodySmall.fontSize};
|
||||
`,
|
||||
legendSymbol: css`
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
margin: auto;
|
||||
margin-right: 4px;
|
||||
`,
|
||||
gradientContainer: css`
|
||||
min-width: 200px;
|
||||
display: flex;
|
||||
|
@ -56,7 +56,9 @@ export const markersLayer: MapLayerRegistryItem<MarkersConfig> = {
|
||||
|
||||
/**
|
||||
* Function that configures transformation and returns a transformer
|
||||
* @param map
|
||||
* @param options
|
||||
* @param theme
|
||||
*/
|
||||
create: async (map: Map, options: MapLayerOptions<MarkersConfig>, theme: GrafanaTheme2) => {
|
||||
// Assert default values
|
||||
@ -137,8 +139,9 @@ export const markersLayer: MapLayerRegistryItem<MarkersConfig> = {
|
||||
// Post updates to the legend component
|
||||
if (legend) {
|
||||
legendProps.next({
|
||||
color: style.dims?.color,
|
||||
styleConfig: style,
|
||||
size: style.dims?.size,
|
||||
layerName: options.name,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ export const defaultBaseLayer: MapLayerRegistryItem = {
|
||||
if (serverLayerType) {
|
||||
const layer = geomapLayerRegistry.getIfExists(serverLayerType);
|
||||
if (!layer) {
|
||||
throw new Error('Invalid basemap configuraiton on server');
|
||||
throw new Error('Invalid basemap configuration on server');
|
||||
}
|
||||
return layer.create(map, config.geomapDefaultBaseLayerConfig!, theme);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user