2022-07-25 19:12:20 -05:00
|
|
|
import { css, cx } from '@emotion/css';
|
|
|
|
import BaseLayer from 'ol/layer/Base';
|
2022-04-18 09:19:20 -05:00
|
|
|
import React, { useMemo } from 'react';
|
2022-07-25 19:12:20 -05:00
|
|
|
import { useObservable } from 'react-use';
|
|
|
|
import { of } from 'rxjs';
|
|
|
|
|
2022-04-18 09:19:20 -05:00
|
|
|
import { DataFrame, formattedValueToString, getFieldColorModeForField, GrafanaTheme2 } from '@grafana/data';
|
2022-07-25 19:12:20 -05:00
|
|
|
import { getMinMaxAndDelta } from '@grafana/data/src/field/scale';
|
|
|
|
import { useStyles2, VizLegendItem } from '@grafana/ui';
|
|
|
|
import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
|
2023-01-25 12:37:29 -06:00
|
|
|
import { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG';
|
2023-01-26 01:03:59 -06:00
|
|
|
import { getThresholdItems } from 'app/core/components/TimelineChart/utils';
|
2021-07-26 17:50:15 -05:00
|
|
|
import { config } from 'app/core/config';
|
2021-08-17 15:43:54 -05:00
|
|
|
import { DimensionSupplier } from 'app/features/dimensions';
|
2022-07-25 19:12:20 -05:00
|
|
|
|
|
|
|
import { StyleConfigState } from '../style/types';
|
|
|
|
import { MapLayerState } from '../types';
|
2021-07-26 17:50:15 -05:00
|
|
|
|
|
|
|
export interface MarkersLegendProps {
|
|
|
|
size?: DimensionSupplier<number>;
|
2022-03-30 09:41:13 -05:00
|
|
|
layerName?: string;
|
|
|
|
styleConfig?: StyleConfigState;
|
2022-04-18 09:19:20 -05:00
|
|
|
layer?: BaseLayer;
|
2021-07-26 17:50:15 -05:00
|
|
|
}
|
2021-08-26 03:17:03 -05:00
|
|
|
|
2021-07-26 17:50:15 -05:00
|
|
|
export function MarkersLegend(props: MarkersLegendProps) {
|
2022-04-18 09:19:20 -05:00
|
|
|
const { layerName, styleConfig, layer } = props;
|
2022-05-03 15:03:41 -05:00
|
|
|
const style = useStyles2(getStyles);
|
2021-08-26 03:17:03 -05:00
|
|
|
|
2022-04-18 09:19:20 -05:00
|
|
|
const hoverEvent = useObservable(((layer as any)?.__state as MapLayerState)?.mouseEvents ?? of(undefined));
|
|
|
|
|
|
|
|
const colorField = styleConfig?.dims?.color?.field;
|
|
|
|
const hoverValue = useMemo(() => {
|
|
|
|
if (!colorField || !hoverEvent) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const props = hoverEvent.getProperties();
|
2022-09-06 20:22:34 -05:00
|
|
|
const frame = props.frame as DataFrame; // eslint-disable-line
|
2022-04-18 09:19:20 -05:00
|
|
|
|
|
|
|
if (!frame) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2022-09-06 20:22:34 -05:00
|
|
|
const rowIndex = props.rowIndex as number; // eslint-disable-line
|
2022-04-18 09:19:20 -05:00
|
|
|
return colorField.values.get(rowIndex);
|
|
|
|
}, [hoverEvent, colorField]);
|
|
|
|
|
2022-03-30 09:41:13 -05:00
|
|
|
if (!styleConfig) {
|
2021-08-26 03:17:03 -05:00
|
|
|
return <></>;
|
2021-07-26 17:50:15 -05:00
|
|
|
}
|
2022-04-18 09:19:20 -05:00
|
|
|
|
2022-07-25 19:12:20 -05:00
|
|
|
const { color, opacity } = styleConfig?.base ?? {};
|
2022-03-30 09:41:13 -05:00
|
|
|
const symbol = styleConfig?.config.symbol?.fixed;
|
2021-08-26 03:17:03 -05:00
|
|
|
|
2022-03-30 09:41:13 -05:00
|
|
|
if (color && symbol && !colorField) {
|
|
|
|
return (
|
|
|
|
<div className={style.infoWrap}>
|
2022-05-03 15:03:41 -05:00
|
|
|
<div className={style.layerName}>{layerName}</div>
|
|
|
|
<div className={cx(style.layerBody, style.fixedColorContainer)}>
|
2023-01-25 12:37:29 -06:00
|
|
|
<SanitizedSVG
|
2022-03-30 09:41:13 -05:00
|
|
|
src={`public/${symbol}`}
|
|
|
|
className={style.legendSymbol}
|
|
|
|
title={'Symbol'}
|
|
|
|
style={{ fill: color, opacity: opacity }}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-25 19:12:20 -05:00
|
|
|
);
|
2022-03-30 09:41:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!colorField) {
|
|
|
|
return <></>;
|
|
|
|
}
|
|
|
|
|
|
|
|
const colorMode = getFieldColorModeForField(colorField);
|
2021-08-26 03:17:03 -05:00
|
|
|
|
2021-07-26 17:50:15 -05:00
|
|
|
if (colorMode.isContinuous && colorMode.getColors) {
|
2021-08-26 03:17:03 -05:00
|
|
|
const colors = colorMode.getColors(config.theme2);
|
2022-03-30 09:41:13 -05:00
|
|
|
const colorRange = getMinMaxAndDelta(colorField);
|
2021-07-26 17:50:15 -05:00
|
|
|
// TODO: explore showing mean on the gradiant scale
|
|
|
|
// const stats = reduceField({
|
|
|
|
// field: color.field!,
|
|
|
|
// reducers: [
|
|
|
|
// ReducerID.min,
|
|
|
|
// ReducerID.max,
|
|
|
|
// ReducerID.mean,
|
|
|
|
// // std dev?
|
|
|
|
// ]
|
|
|
|
// })
|
|
|
|
|
2022-07-25 19:12:20 -05:00
|
|
|
const display = colorField.display
|
|
|
|
? (v: number) => formattedValueToString(colorField.display!(v))
|
|
|
|
: (v: number) => `${v}`;
|
2021-08-26 03:17:03 -05:00
|
|
|
return (
|
2022-05-03 15:03:41 -05:00
|
|
|
<div className={style.infoWrap}>
|
|
|
|
<div className={style.layerName}>{layerName}</div>
|
|
|
|
<div className={cx(style.layerBody, style.colorScaleWrapper)}>
|
2022-07-25 19:12:20 -05:00
|
|
|
<ColorScale
|
|
|
|
hoverValue={hoverValue}
|
|
|
|
colorPalette={colors}
|
|
|
|
min={colorRange.min as number}
|
|
|
|
max={colorRange.max as number}
|
|
|
|
display={display}
|
|
|
|
useStopsPercentage={false}
|
|
|
|
/>
|
2021-08-26 03:17:03 -05:00
|
|
|
</div>
|
2022-05-03 15:03:41 -05:00
|
|
|
</div>
|
2021-08-26 03:17:03 -05:00
|
|
|
);
|
2021-07-26 17:50:15 -05:00
|
|
|
}
|
|
|
|
|
2022-03-30 09:41:13 -05:00
|
|
|
const thresholds = colorField?.config?.thresholds;
|
2021-08-31 01:22:19 -05:00
|
|
|
if (!thresholds || thresholds.steps.length < 2) {
|
|
|
|
return <div></div>; // don't show anything in the legend
|
2021-07-26 17:50:15 -05:00
|
|
|
}
|
|
|
|
|
2022-03-30 09:41:13 -05:00
|
|
|
const items = getThresholdItems(colorField!.config, config.theme2);
|
2021-07-26 17:50:15 -05:00
|
|
|
return (
|
|
|
|
<div className={style.infoWrap}>
|
2022-05-03 15:03:41 -05:00
|
|
|
<div className={style.layerName}>{layerName}</div>
|
|
|
|
<div className={cx(style.layerBody, style.legend)}>
|
2021-09-01 10:43:57 -05:00
|
|
|
{items.map((item: VizLegendItem, idx: number) => (
|
|
|
|
<div key={`${idx}/${item.label}`} className={style.legendItem}>
|
|
|
|
<i style={{ background: item.color }}></i>
|
|
|
|
{item.label}
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
2021-07-26 17:50:15 -05:00
|
|
|
</div>
|
2021-08-26 03:17:03 -05:00
|
|
|
);
|
2021-07-26 17:50:15 -05:00
|
|
|
}
|
|
|
|
|
2022-05-03 15:03:41 -05:00
|
|
|
const getStyles = (theme: GrafanaTheme2) => ({
|
2021-07-26 17:50:15 -05:00
|
|
|
infoWrap: css`
|
2022-05-03 15:03:41 -05:00
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
2021-08-26 03:17:03 -05:00
|
|
|
background: ${theme.colors.background.secondary};
|
2022-05-03 15:03:41 -05:00
|
|
|
border-radius: 1px;
|
2021-08-26 03:17:03 -05:00
|
|
|
padding: ${theme.spacing(1)};
|
2022-05-03 15:03:41 -05:00
|
|
|
border-bottom: 2px solid ${theme.colors.border.strong};
|
|
|
|
min-width: 150px;
|
|
|
|
`,
|
|
|
|
layerName: css`
|
|
|
|
font-size: ${theme.typography.body.fontSize};
|
|
|
|
`,
|
|
|
|
layerBody: css`
|
|
|
|
padding-left: 10px;
|
2021-07-26 17:50:15 -05:00
|
|
|
`,
|
|
|
|
legend: css`
|
|
|
|
line-height: 18px;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
2021-08-26 03:17:03 -05:00
|
|
|
font-size: ${theme.typography.bodySmall.fontSize};
|
2022-05-03 15:03:41 -05:00
|
|
|
padding: 5px 10px 0;
|
2021-07-26 17:50:15 -05:00
|
|
|
|
|
|
|
i {
|
2022-05-16 21:54:10 -05:00
|
|
|
width: 15px;
|
|
|
|
height: 15px;
|
2021-07-26 17:50:15 -05:00
|
|
|
float: left;
|
|
|
|
margin-right: 8px;
|
|
|
|
opacity: 0.7;
|
2022-05-16 21:54:10 -05:00
|
|
|
border-radius: 50%;
|
2021-07-26 17:50:15 -05:00
|
|
|
}
|
|
|
|
`,
|
|
|
|
legendItem: css`
|
|
|
|
white-space: nowrap;
|
|
|
|
`,
|
2022-03-30 09:41:13 -05:00
|
|
|
fixedColorContainer: css`
|
|
|
|
min-width: 80px;
|
|
|
|
font-size: ${theme.typography.bodySmall.fontSize};
|
2022-05-03 15:03:41 -05:00
|
|
|
padding-top: 5px;
|
2022-03-30 09:41:13 -05:00
|
|
|
`,
|
|
|
|
legendSymbol: css`
|
2022-05-16 21:54:10 -05:00
|
|
|
height: 18px;
|
|
|
|
width: 18px;
|
2022-03-30 09:41:13 -05:00
|
|
|
margin: auto;
|
|
|
|
`,
|
2022-04-18 09:19:20 -05:00
|
|
|
colorScaleWrapper: css`
|
2021-07-26 17:50:15 -05:00
|
|
|
min-width: 200px;
|
2021-08-26 03:17:03 -05:00
|
|
|
font-size: ${theme.typography.bodySmall.fontSize};
|
2022-05-03 15:03:41 -05:00
|
|
|
padding-top: 10px;
|
2021-08-26 03:17:03 -05:00
|
|
|
`,
|
2022-05-03 15:03:41 -05:00
|
|
|
});
|