From 59ec3cc8a9f279e6395374e051513e833014ecc7 Mon Sep 17 00:00:00 2001 From: Paul Marbach Date: Mon, 15 Dec 2025 22:29:53 -0500 Subject: [PATCH] wip: progress on everything --- .../components/RadialGauge/RadialArcPath.tsx | 3 +- .../src/components/RadialGauge/RadialBar.tsx | 2 - .../RadialGauge/RadialBarSegmented.tsx | 19 ++-- .../RadialGauge/RadialColorDefs.tsx | 100 +----------------- .../components/RadialGauge/RadialGauge.tsx | 2 + .../components/RadialGauge/ThresholdsBar.tsx | 15 +-- 6 files changed, 25 insertions(+), 116 deletions(-) diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx index abf142bc46c..7492bc27199 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx @@ -1,4 +1,4 @@ -import { useId } from 'react'; +import { useId, useEffect } from 'react'; import { RadialColorDefs } from './RadialColorDefs'; import { RadialShape } from './RadialGauge'; @@ -11,7 +11,6 @@ export interface RadialArcPathPropsBase { arcLengthDeg: number; shape: RadialShape; color?: string; - gradient?: string; glowFilter?: string; roundedBars?: boolean; showGuideDots?: boolean; diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx index 729f60ed630..f3d5d987d55 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx @@ -47,7 +47,6 @@ export function RadialBar({ startAngle={startAngle} arcLengthDeg={angle} colorDefs={colorDefs} - gradient={colorDefs.getGradientDef()} roundedBars={roundedBars} glowFilter={glowFilter} showGuideDots={roundedBars} @@ -56,7 +55,6 @@ export function RadialBar({ shape={shape} /> - {colorDefs.getDefs()} ); } diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx index dbd668f3a00..e6858b6cec2 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx @@ -4,7 +4,7 @@ import { useTheme2 } from '../../themes/ThemeContext'; import { RadialArcPath } from './RadialArcPath'; import { RadialColorDefs } from './RadialColorDefs'; -import { RadialShape } from './RadialGauge'; +import { RadialGradientMode, RadialShape } from './RadialGauge'; import { GaugeDimensions } from './utils'; export interface RadialBarSegmentedProps { @@ -17,6 +17,7 @@ export interface RadialBarSegmentedProps { segmentCount: number; segmentSpacing: number; shape: RadialShape; + gradientMode: RadialGradientMode; } export function RadialBarSegmented({ fieldDisplay, @@ -28,6 +29,7 @@ export function RadialBarSegmented({ segmentSpacing, colorDefs, shape, + gradientMode, }: RadialBarSegmentedProps) { const segments: React.ReactNode[] = []; const theme = useTheme2(); @@ -41,9 +43,13 @@ export function RadialBarSegmented({ for (let i = 0; i < segmentCountAdjusted; i++) { const angleValue = min + ((max - min) / segmentCountAdjusted) * i; - // const angleColor = colorDefs.getSegmentColor(angleValue, i); const segmentAngle = startAngle + (angleRange / segmentCountAdjusted) * i + 0.01; - const segmentColor = angleValue >= value ? theme.colors.action.hover : undefined; + let segmentColor: string | undefined; + if (angleValue >= value) { + segmentColor = theme.colors.action.hover; + } else if (gradientMode === 'none') { + segmentColor = colorDefs.getSegmentColor(angleValue); + } segments.push( - {segments} - {colorDefs.getDefs()} - - ); + return {segments}; } export function getAngleBetweenSegments(segmentSpacing: number, segmentCount: number, range: number) { diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx index 87a6a0d4ba8..fabd4b8dd3f 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx @@ -1,11 +1,4 @@ -import { - colorManipulator, - DisplayProcessor, - FALLBACK_COLOR, - FieldDisplay, - getFieldColorMode, - GrafanaTheme2, -} from '@grafana/data'; +import { colorManipulator, DisplayProcessor, FALLBACK_COLOR, FieldDisplay, GrafanaTheme2 } from '@grafana/data'; import { RadialGradientMode, RadialShape } from './RadialGauge'; import { buildGradientColors } from './colors'; @@ -29,98 +22,17 @@ const getGuideDotColor = (color: string): string => { }; export class RadialColorDefs { - private colorToIds: Record = {}; - private defs: React.ReactNode[] = []; - constructor(private options: RadialColorDefsOptions) {} - getSegmentColor(forValue: number, segmentIdx: number): string { + getSegmentColor(forValue: number): string { const { displayProcessor } = this.options; - const baseColor = displayProcessor(forValue).color ?? FALLBACK_COLOR; - return this.getColor(baseColor, segmentIdx); - } - - getColor(baseColor: string, segmentIdx?: number): string { - const { gradient, dimensions, gaugeId, fieldDisplay, shape } = this.options; - - let id = `value-color-${baseColor}-${gaugeId}`; - const forSegment = segmentIdx !== undefined; - if (forSegment) { - id += `-segment-${segmentIdx}`; - } - - if (this.colorToIds[id]) { - return this.colorToIds[id]; - } - - // If no gradient, just return the base color - if (gradient === 'none') { - this.colorToIds[id] = baseColor; - return baseColor; - } - - const returnColor = (this.colorToIds[id] = `url(#${id})`); - const colorModeId = fieldDisplay.field.color?.mode; - const colorMode = getFieldColorMode(colorModeId); - const valuePercent = fieldDisplay.display.percent ?? 0; - - const gradientStops = this.getGradient(baseColor, forSegment); - const stops = gradientStops.map((stop, i) => ( - - )); - - // circular gradients are a little awkward today. we don't exactly have the result we - // want for continuous color modes, which would be to have the radial bar fill from the top - // around the circle. But SVG doesn't support that kind of gradient on stroke paths out-of-the-box, - // we'd need to implement something like https://gist.github.com/mbostock/4163057 - - // Handle continusous color modes first - // If it's a segment color we don't want to do continuous gradients - if (colorMode.isContinuous && colorMode.getColors && !forSegment) { - this.defs.push( - - {stops} - - ); - - return returnColor; - } - - // 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 - if (colorMode.isByValue) { - const x2 = shape === 'circle' ? 0 : 1 / valuePercent; - const y2 = shape === 'circle' ? 1 : 0; - this.defs.push( - - {stops} - - ); - return returnColor; - } - - // For fixed / palette based color scales we can create a more fun - // hue and light based linear gradient that we rotate/move with the value - const x2 = shape === 'circle' ? 0 : dimensions.centerX + dimensions.radius; - const y2 = shape === 'circle' ? dimensions.centerY + dimensions.radius : 0; - - this.defs.push( - - {stops} - - ); - - return returnColor; + return displayProcessor(forValue).color ?? FALLBACK_COLOR; } getFieldBaseColor(): string { return this.options.fieldDisplay.display.color ?? FALLBACK_COLOR; } - getMainBarColor(): string { - return this.getColor(this.getFieldBaseColor()); - } - getGradient(baseColor = this.getFieldBaseColor(), forSegment?: boolean): Array<{ color: string; percent: number }> { const { displayProcessor, gradient, fieldDisplay, theme } = this.options; return buildGradientColors(gradient, baseColor, theme, displayProcessor, fieldDisplay, forSegment); @@ -131,7 +43,7 @@ export class RadialColorDefs { const colorStrings = gradientStops.map((stop) => `${stop.color} ${(stop.percent * 100).toFixed(2)}%`); return this.options.shape === 'circle' ? `conic-gradient(from 0deg, ${colorStrings.join(', ')})` - : 'linear-gradient(90deg, ' + colorStrings.join(', ') + ')'; + : `linear-gradient(90deg, ${colorStrings.join(', ')})`; } getEndpointColors(): [string, string] { @@ -157,8 +69,4 @@ export class RadialColorDefs { const [startColor, endColor] = this.getEndpointColors(); return [getGuideDotColor(startColor), getGuideDotColor(endColor)]; } - - getDefs(): React.ReactNode[] { - return this.defs; - } } diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx index fc03f9845e3..97048b6674a 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx @@ -156,6 +156,7 @@ export function RadialGauge(props: RadialGaugeProps) { segmentSpacing={segmentSpacing} colorDefs={colorDefs} shape={shape} + gradientMode={gradient} /> ); } else { @@ -231,6 +232,7 @@ export function RadialGauge(props: RadialGaugeProps) { glowFilter={`url(#${glowFilterId})`} colorDefs={colorDefs} shape={shape} + gradientMode={gradient} /> ); } diff --git a/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx b/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx index 15e5349e8a5..bf9ebdcc46a 100644 --- a/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx @@ -1,8 +1,10 @@ import { FieldDisplay, Threshold } from '@grafana/data'; +import { useTheme2 } from '../../themes/ThemeContext'; + import { RadialArcPath } from './RadialArcPath'; import { RadialColorDefs } from './RadialColorDefs'; -import { RadialShape } from './RadialGauge'; +import { RadialGradientMode, RadialShape } from './RadialGauge'; import { GaugeDimensions } from './utils'; export interface Props { @@ -16,6 +18,7 @@ export interface Props { glowFilter?: string; colorDefs: RadialColorDefs; thresholds: Threshold[]; + gradientMode: RadialGradientMode; } export function ThresholdsBar({ dimensions, @@ -27,7 +30,9 @@ export function ThresholdsBar({ colorDefs, thresholds, shape, + gradientMode, }: Props) { + const theme = useTheme2(); const fieldConfig = fieldDisplay.field; const min = fieldConfig.min ?? 0; const max = fieldConfig.max ?? 100; @@ -59,6 +64,7 @@ export function ThresholdsBar({ startAngle={currentStart} arcLengthDeg={lengthDeg} colorDefs={colorDefs} + color={gradientMode === 'none' ? threshold.color : undefined} shape={shape} dimensions={thresholdDimensions} roundedBars={roundedBars} @@ -69,10 +75,5 @@ export function ThresholdsBar({ currentStart += lengthDeg; } - return ( - <> - {paths} - {colorDefs.getDefs()} - - ); + return {paths}; }