Timeline: Use theme getContrastText to calculate value color based on background color (#34072)

* Timeline: Use theme getContrastText to calculate value color based on background color

* Changed back to using the old functions fill, and stroke
This commit is contained in:
Torkel Ödegaard 2021-05-13 21:41:40 +02:00 committed by GitHub
parent 36c4083264
commit 917a9ace57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 21 deletions

View File

@ -73,7 +73,7 @@ export interface ThemeHoverStrengh {}
/** @beta */
export interface ThemeColors extends ThemeColorsBase<ThemeRichColor> {
/** Returns a text color for the background */
getContrastText(background: string): string;
getContrastText(background: string, threshold?: number): string;
/* Brighten or darken a color by specified factor (0-1) */
emphasize(color: string, amount?: number): string;
}
@ -255,11 +255,9 @@ export function createColors(colors: ThemeColorsInput): ThemeColors {
...other
} = colors;
function getContrastText(background: string) {
function getContrastText(background: string, threshold: number = contrastThreshold) {
const contrastText =
getContrastRatio(background, dark.text.maxContrast) >= contrastThreshold
? dark.text.maxContrast
: light.text.maxContrast;
getContrastRatio(background, dark.text.maxContrast) >= threshold ? dark.text.maxContrast : light.text.maxContrast;
// todo, need color framework
return contrastText;
}

View File

@ -3,7 +3,7 @@ import { FIXED_UNIT } from '@grafana/ui/src/components/GraphNG/GraphNG';
import { Quadtree, Rect, pointWithin } from 'app/plugins/panel/barchart/quadtree';
import { distribute, SPACE_BETWEEN } from 'app/plugins/panel/barchart/distribute';
import { TimelineMode } from './types';
import { TimeRange } from '@grafana/data';
import { GrafanaTheme2, TimeRange } from '@grafana/data';
import { BarValueVisibility } from '@grafana/ui';
const { round, min, ceil } = Math;
@ -12,8 +12,6 @@ const pxRatio = devicePixelRatio;
const laneDistr = SPACE_BETWEEN;
const font = Math.round(10 * pxRatio) + 'px Roboto';
type WalkCb = (idx: number, offPx: number, dimPx: number) => void;
function walk(rowHeight: number, yIdx: number | null, count: number, dim: number, draw: WalkCb) {
@ -33,12 +31,13 @@ export interface TimelineCoreOptions {
numSeries: number;
rowHeight: number;
colWidth?: number;
theme: GrafanaTheme2;
showValue: BarValueVisibility;
isDiscrete: (seriesIdx: number) => boolean;
colorLookup: (seriesIdx: number, value: any) => string;
label: (seriesIdx: number) => string;
fill: (seriesIdx: number, valueIdx: number, value: any) => CanvasRenderingContext2D['fillStyle'];
stroke: (seriesIdx: number, valueIdx: number, value: any) => CanvasRenderingContext2D['strokeStyle'];
fill: (seriesIdx: number, value: any) => CanvasRenderingContext2D['fillStyle'];
stroke: (seriesIdx: number, value: any) => CanvasRenderingContext2D['strokeStyle'];
getTimeRange: () => TimeRange;
formatValue?: (seriesIdx: number, value: any) => string;
onHover?: (seriesIdx: number, valueIdx: number) => void;
@ -56,11 +55,13 @@ export function getConfig(opts: TimelineCoreOptions) {
rowHeight = 0,
colWidth = 0,
showValue,
theme,
label,
fill,
stroke,
formatValue,
getTimeRange,
colorLookup,
// onHover,
// onLeave,
} = opts;
@ -73,10 +74,11 @@ export function getConfig(opts: TimelineCoreOptions) {
let mark = document.createElement('div');
mark.classList.add('bar-mark');
mark.style.position = 'absolute';
mark.style.background = 'rgba(255,255,255,0.4)';
mark.style.background = 'rgba(255,255,255,0.2)';
return mark;
});
const font = `500 ${Math.round(12 * devicePixelRatio)}px ${theme.typography.fontFamily}`;
const hovered: Array<Rect | null> = Array(numSeries).fill(null);
const size = [colWidth, Infinity];
@ -117,7 +119,7 @@ export function getConfig(opts: TimelineCoreOptions) {
discrete: boolean
) {
if (discrete) {
let fillStyle = fill(seriesIdx + 1, valueIdx, value);
let fillStyle = fill(seriesIdx + 1, value);
let fillPath = fillPaths.get(fillStyle);
if (fillPath == null) {
@ -127,7 +129,7 @@ export function getConfig(opts: TimelineCoreOptions) {
rect(fillPath, lft, top, wid, hgt);
if (strokeWidth) {
let strokeStyle = stroke(seriesIdx + 1, valueIdx, value);
let strokeStyle = stroke(seriesIdx + 1, value);
let strokePath = strokePaths.get(strokeStyle);
if (strokePath == null) {
@ -139,13 +141,13 @@ export function getConfig(opts: TimelineCoreOptions) {
} else {
ctx.beginPath();
rect(ctx, lft, top, wid, hgt);
ctx.fillStyle = fill(seriesIdx, valueIdx, value);
ctx.fillStyle = fill(seriesIdx, value);
ctx.fill();
if (strokeWidth) {
ctx.beginPath();
rect(ctx, lft + strokeWidth / 2, top + strokeWidth / 2, wid - strokeWidth, hgt - strokeWidth);
ctx.strokeStyle = stroke(seriesIdx, valueIdx, value);
ctx.strokeStyle = stroke(seriesIdx, value);
ctx.stroke();
}
}
@ -195,7 +197,7 @@ export function getConfig(opts: TimelineCoreOptions) {
yOff,
lft,
round(yOff + y0),
rgt - lft,
rgt - lft - 2,
round(hgt),
strokeWidth,
iy,
@ -257,7 +259,6 @@ export function getConfig(opts: TimelineCoreOptions) {
u.ctx.clip();
u.ctx.font = font;
u.ctx.fillStyle = 'black';
u.ctx.textAlign = mode === TimelineMode.Changes ? 'left' : 'center';
u.ctx.textBaseline = 'middle';
@ -285,6 +286,15 @@ export function getConfig(opts: TimelineCoreOptions) {
for (let ix = 0; ix < dataY.length; ix++) {
if (dataY[ix] != null) {
let x = valToPosX(dataX[ix], scaleX, xDim, xOff);
// For the left aligned values shift them 2 pixels of edge
if (mode === TimelineMode.Changes) {
x += 2;
}
const valueColor = colorLookup(sidx, dataY[ix]);
u.ctx.fillStyle = theme.colors.getContrastText(valueColor, 3);
u.ctx.fillText(formatValue(sidx, dataY[ix]), x, y);
}
}

View File

@ -6,8 +6,8 @@ import {
formattedValueToString,
getFieldDisplayName,
outerJoinDataFrames,
classicColors,
Field,
FALLBACK_COLOR,
} from '@grafana/data';
import {
UPlotConfigBuilder,
@ -72,15 +72,17 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
return !(mode && field.display && mode.startsWith('continuous-'));
};
const colorLookup = (seriesIdx: number, valueIdx: number, value: any) => {
const colorLookup = (seriesIdx: number, value: any) => {
const field = frame.fields[seriesIdx];
if (field.display) {
const disp = field.display(value); // will apply color modes
if (disp.color) {
return disp.color;
}
}
return classicColors[Math.floor(value % classicColors.length)];
return FALLBACK_COLOR;
};
const yAxisWidth =
@ -99,9 +101,11 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
rowHeight: rowHeight!,
colWidth: colWidth,
showValue: showValue!,
theme,
label: (seriesIdx) => getFieldDisplayName(frame.fields[seriesIdx], frame),
fill: colorLookup,
stroke: colorLookup,
colorLookup,
getTimeRange,
// hardcoded formatter for state values
formatValue: (seriesIdx, value) => formattedValueToString(frame.fields[seriesIdx].display!(value)),