diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx index b1a068c93a6..abf142bc46c 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx @@ -1,15 +1,15 @@ import { useId } from 'react'; -import { colorManipulator } from '@grafana/data'; - import { RadialColorDefs } from './RadialColorDefs'; +import { RadialShape } from './RadialGauge'; import { GaugeDimensions, toRad } from './utils'; export interface RadialArcPathPropsBase { startAngle: number; dimensions: GaugeDimensions; - colorDef: RadialColorDefs; + colorDefs: RadialColorDefs; arcLengthDeg: number; + shape: RadialShape; color?: string; gradient?: string; glowFilter?: string; @@ -116,9 +116,10 @@ export function RadialArcPath({ startAngle: angle, dimensions, color, - colorDef, - gradient, + colorDefs, + shape, arcLengthDeg, + glowFilter, roundedBars, showGuideDots, guideDotStartColor, @@ -129,7 +130,7 @@ export function RadialArcPath({ const startRadians = toRad(angle); const endRadians = toRad(angle + arcLengthDeg); - const gradientList = colorDef?.getGradient(); + const [startColor, endColor] = colorDefs.getEndpointColors(); const path = drawRadialArcPath({ angle, arcLengthDeg, dimensions, roundedBars }); let x1 = centerX + radius * Math.cos(startRadians); @@ -144,19 +145,34 @@ export function RadialArcPath({ - -
- + + +
+ + - {roundedBars && gradientList && ( - <> - - {/* this would actually need to be the color determined by the display */} - - - )} {showGuideDots && ( <> + {shape === 'circle' && ( + <> + + + + )} + {arcLengthDeg > 5 && } diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx index bb0dac60d45..729f60ed630 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx @@ -2,6 +2,7 @@ import { useTheme2 } from '../../themes/ThemeContext'; import { RadialArcPath } from './RadialArcPath'; import { RadialColorDefs } from './RadialColorDefs'; +import { RadialShape } from './RadialGauge'; import { GaugeDimensions } from './utils'; export interface RadialBarProps { @@ -12,6 +13,7 @@ export interface RadialBarProps { startAngle: number; roundedBars?: boolean; glowFilter?: string; + shape: RadialShape; } export function RadialBar({ dimensions, @@ -21,6 +23,7 @@ export function RadialBar({ startAngle, roundedBars, glowFilter, + shape, }: RadialBarProps) { const theme = useTheme2(); const [startDotColor, endDotColor] = colorDefs.getGuideDotColors(); @@ -33,20 +36,24 @@ export function RadialBar({ startAngle={startAngle + angle} dimensions={dimensions} arcLengthDeg={angleRange - angle} + colorDefs={colorDefs} color={theme.colors.action.hover} roundedBars={roundedBars} + shape={shape} /> {/** The colored bar */} {colorDefs.getDefs()} diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx index dd2ec0ac9bf..dbd668f3a00 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx @@ -4,6 +4,7 @@ import { useTheme2 } from '../../themes/ThemeContext'; import { RadialArcPath } from './RadialArcPath'; import { RadialColorDefs } from './RadialColorDefs'; +import { RadialShape } from './RadialGauge'; import { GaugeDimensions } from './utils'; export interface RadialBarSegmentedProps { @@ -15,6 +16,7 @@ export interface RadialBarSegmentedProps { glowFilter?: string; segmentCount: number; segmentSpacing: number; + shape: RadialShape; } export function RadialBarSegmented({ fieldDisplay, @@ -25,6 +27,7 @@ export function RadialBarSegmented({ segmentCount, segmentSpacing, colorDefs, + shape, }: RadialBarSegmentedProps) { const segments: React.ReactNode[] = []; const theme = useTheme2(); @@ -38,9 +41,9 @@ export function RadialBarSegmented({ for (let i = 0; i < segmentCountAdjusted; i++) { const angleValue = min + ((max - min) / segmentCountAdjusted) * i; - const angleColor = colorDefs.getSegmentColor(angleValue, i); + // const angleColor = colorDefs.getSegmentColor(angleValue, i); const segmentAngle = startAngle + (angleRange / segmentCountAdjusted) * i + 0.01; - const segmentColor = angleValue >= value ? theme.colors.action.hover : angleColor; + const segmentColor = angleValue >= value ? theme.colors.action.hover : undefined; segments.push( diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx index 97b24b99054..87a6a0d4ba8 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx @@ -122,35 +122,24 @@ export class RadialColorDefs { } getGradient(baseColor = this.getFieldBaseColor(), forSegment?: boolean): Array<{ color: string; percent: number }> { - const { gradient, fieldDisplay, theme } = this.options; - return buildGradientColors(gradient, baseColor, theme, fieldDisplay.field.color?.mode, forSegment); + const { displayProcessor, gradient, fieldDisplay, theme } = this.options; + return buildGradientColors(gradient, baseColor, theme, displayProcessor, fieldDisplay, forSegment); } getGradientDef(): string { const gradientStops = this.getGradient(); const colorStrings = gradientStops.map((stop) => `${stop.color} ${(stop.percent * 100).toFixed(2)}%`); return this.options.shape === 'circle' - ? `conic-gradient(from -10deg, ${colorStrings.join(', ')})` + ? `conic-gradient(from 0deg, ${colorStrings.join(', ')})` : 'linear-gradient(90deg, ' + colorStrings.join(', ') + ')'; } - getGuideDotColors(): [string, string] { - const { dimensions, fieldDisplay, shape } = this.options; + getEndpointColors(): [string, string] { + const { fieldDisplay } = this.options; const gradient = this.getGradient(); - let valuePercent = fieldDisplay.display.percent ?? 0; - - // the linear gradient used in circular gradients means that we want to use the - // y position of the edge of the bar to determine the color. If we ever address - // that shortcoming, we could delete this block. - if (shape === 'circle') { - const angleDeg = ((valuePercent - 0.25) % 1) * 360; - const angleRad = (angleDeg * Math.PI) / 180; - const yPos = dimensions.centerY + dimensions.radius * Math.sin(angleRad); - valuePercent = yPos / (dimensions.centerY * 2); - } - - let startColor = gradient[0].color; + const valuePercent = fieldDisplay.display.percent ?? 0; + const startColor = gradient[0].color; let endColor = gradient[gradient.length - 1].color; // if we have a percentageFilled, use it to get a the correct end color based on where the bar terminates @@ -161,7 +150,11 @@ export class RadialColorDefs { ? endColorByPercentage.toHexString() : endColorByPercentage.toHex8String(); } + return [startColor, endColor]; + } + getGuideDotColors(): [string, string] { + const [startColor, endColor] = this.getEndpointColors(); return [getGuideDotColor(startColor), getGuideDotColor(endColor)]; } diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx index d20665872c7..fc03f9845e3 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx @@ -155,6 +155,7 @@ export function RadialGauge(props: RadialGaugeProps) { segmentCount={segmentCount} segmentSpacing={segmentSpacing} colorDefs={colorDefs} + shape={shape} /> ); } else { @@ -168,6 +169,7 @@ export function RadialGauge(props: RadialGaugeProps) { startAngle={startAngle} roundedBars={roundedBars} glowFilter={`url(#${glowFilterId})`} + shape={shape} /> ); } @@ -228,6 +230,7 @@ export function RadialGauge(props: RadialGaugeProps) { roundedBars={roundedBars} glowFilter={`url(#${glowFilterId})`} colorDefs={colorDefs} + shape={shape} /> ); } diff --git a/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx b/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx index 8977bbb6642..15e5349e8a5 100644 --- a/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx @@ -2,6 +2,7 @@ import { FieldDisplay, Threshold } from '@grafana/data'; import { RadialArcPath } from './RadialArcPath'; import { RadialColorDefs } from './RadialColorDefs'; +import { RadialShape } from './RadialGauge'; import { GaugeDimensions } from './utils'; export interface Props { @@ -9,6 +10,7 @@ export interface Props { angleRange: number; startAngle: number; endAngle: number; + shape: RadialShape; fieldDisplay: FieldDisplay; roundedBars?: boolean; glowFilter?: string; @@ -24,6 +26,7 @@ export function ThresholdsBar({ glowFilter, colorDefs, thresholds, + shape, }: Props) { const fieldConfig = fieldDisplay.field; const min = fieldConfig.min ?? 0; @@ -55,10 +58,11 @@ export function ThresholdsBar({ key={i} startAngle={currentStart} arcLengthDeg={lengthDeg} + colorDefs={colorDefs} + shape={shape} dimensions={thresholdDimensions} roundedBars={roundedBars} glowFilter={glowFilter} - color={colorDefs.getColor(threshold.color, i)} /> ); diff --git a/packages/grafana-ui/src/components/RadialGauge/colors.ts b/packages/grafana-ui/src/components/RadialGauge/colors.ts index 62a31470ed3..99e23452898 100644 --- a/packages/grafana-ui/src/components/RadialGauge/colors.ts +++ b/packages/grafana-ui/src/components/RadialGauge/colors.ts @@ -1,6 +1,7 @@ import tinycolor from 'tinycolor2'; -import { FieldColorModeId, getFieldColorMode, GrafanaTheme2 } from '@grafana/data'; +import { DisplayProcessor, FieldDisplay, getFieldColorMode, GrafanaTheme2 } from '@grafana/data'; +import { FieldColorModeId } from '@grafana/schema'; import { RadialGradientMode } from './RadialGauge'; @@ -8,7 +9,8 @@ export function buildGradientColors( gradientMode: RadialGradientMode, baseColor: string, theme: GrafanaTheme2, - colorModeId?: FieldColorModeId | string, + displayProcessor: DisplayProcessor, + fieldDisplay: FieldDisplay, forSegment?: boolean ): Array<{ color: string; percent: number }> { if (gradientMode === 'none') { @@ -18,15 +20,36 @@ export function buildGradientColors( ]; } - const colorMode = getFieldColorMode(colorModeId); + const colorMode = getFieldColorMode(fieldDisplay.field.color?.mode); + + if (colorMode.id === FieldColorModeId.Thresholds) { + const thresholds = fieldDisplay.field.thresholds?.steps ?? []; + const min = fieldDisplay.field.min ?? 0; + const max = fieldDisplay.field.max ?? 100; + + const result: Array<{ color: string; percent: number }> = [ + { color: displayProcessor(min).color ?? baseColor, percent: 0 }, + ]; + + for (const threshold of thresholds) { + if (threshold.value > min && threshold.value < max) { + const percent = (threshold.value - min) / (max - min); + result.push({ color: threshold.color, percent }); + } + } + + result.push({ color: displayProcessor(max).color ?? baseColor, percent: 1 }); + + return result; + } - // TODO we need to return thresholded values here. those will have breakpoints - // which map to exact percentages, and we should show that correctly. - // Handle continuous color modes first if (colorMode.isContinuous && colorMode.getColors && !forSegment) { + // Handle continuous color modes first const colors = colorMode.getColors(theme); return colors.map((color, idx) => ({ color, percent: idx / (colors.length - 1) })); - } else if (colorMode.isByValue) { + } + + if (colorMode.isByValue) { // For value based colors we want to stay more true to the specific color // So a radial gradient that adds a bit of light and shade works best const darkerColor = tinycolor(baseColor).darken(5);