diff --git a/public/app/plugins/panel/geomap/GeomapPanel.tsx b/public/app/plugins/panel/geomap/GeomapPanel.tsx index 4df51e4d1cd..187400ffbfd 100644 --- a/public/app/plugins/panel/geomap/GeomapPanel.tsx +++ b/public/app/plugins/panel/geomap/GeomapPanel.tsx @@ -41,6 +41,7 @@ let sharedView: View | undefined = undefined; type Props = PanelProps; interface State extends OverlayProps { ttip?: GeomapHoverPayload; + legends: ReactNode[]; } export interface GeomapLayerActions { @@ -77,7 +78,7 @@ export class GeomapPanel extends Component { constructor(props: Props) { super(props); - this.state = {}; + this.state = { legends: [] }; this.subs.add( this.props.eventBus.subscribe(PanelEditExitedEvent, (evt) => { if (this.mapDiv && this.props.id === evt.payload) { @@ -109,7 +110,7 @@ export class GeomapPanel extends Component { 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; @@ -119,7 +120,7 @@ export class GeomapPanel extends Component { 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, @@ -128,6 +129,8 @@ export class GeomapPanel extends Component { actions: this.actions, }); } + + this.setState({ legends: this.getLegends() }); } getNextLayerName = () => { @@ -301,6 +304,8 @@ export class GeomapPanel extends Component { actions: this.actions, }); } + + this.setState({ legends: this.getLegends() }); }; clearTooltip = () => { @@ -411,6 +416,9 @@ export class GeomapPanel extends Component { return false; } + // Just to trigger a state update + this.setState({ legends: [] }); + this.layers = layers; this.doOptionsUpdate(layerIndex); return true; @@ -445,6 +453,7 @@ export class GeomapPanel extends Component { if (!options.name) { options.name = this.getNextLayerName(); } + const UID = options.name; const state = { UID, // unique name when added to the map (it may change and will need special handling) @@ -458,6 +467,7 @@ export class GeomapPanel extends Component { this.updateLayer(UID, cfg); }, }; + this.byName.set(UID, state); return state; } @@ -541,15 +551,26 @@ export class GeomapPanel extends Component { this.setState({ topRight }); } + getLegends() { + const legends: ReactNode[] = []; + for (const state of this.layers) { + if (state.handler.legend) { + legends.push(
{state.handler.legend}
); + } + } + + return legends; + } + render() { - const { ttip, topRight, bottomLeft } = this.state; + const { ttip, topRight, legends } = this.state; return ( <>
- +
{ttip && (ttip.data || ttip.feature) && ( diff --git a/public/app/plugins/panel/geomap/layers/data/MarkersLegend.tsx b/public/app/plugins/panel/geomap/layers/data/MarkersLegend.tsx index 7197525cd1c..b403b1fb805 100644 --- a/public/app/plugins/panel/geomap/layers/data/MarkersLegend.tsx +++ b/public/app/plugins/panel/geomap/layers/data/MarkersLegend.tsx @@ -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; size?: DimensionSupplier; + 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 ( +
+
+ + {layerName} +
+
+ ) + } + + 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 ( <> - +
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
; // don't show anything in the legend } - const items = getThresholdItems(color.field!.config, config.theme2); + const items = getThresholdItems(colorField!.config, config.theme2); return (
@@ -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; diff --git a/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx b/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx index 211453025ab..ebb0702b638 100644 --- a/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx +++ b/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx @@ -56,7 +56,9 @@ export const markersLayer: MapLayerRegistryItem = { /** * Function that configures transformation and returns a transformer + * @param map * @param options + * @param theme */ create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => { const matchers = await getLocationMatchers(options.location); @@ -122,8 +124,9 @@ export const markersLayer: MapLayerRegistryItem = { // Post updates to the legend component if (legend) { legendProps.next({ - color: style.dims?.color, + styleConfig: style, size: style.dims?.size, + layerName: options.name, }); } break; // Only the first frame for now! diff --git a/public/app/plugins/panel/geomap/layers/registry.ts b/public/app/plugins/panel/geomap/layers/registry.ts index de99df94e5f..123828a9de1 100644 --- a/public/app/plugins/panel/geomap/layers/registry.ts +++ b/public/app/plugins/panel/geomap/layers/registry.ts @@ -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); }