mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ColorPicker: return values as hex strings and update theme (#37173)
* Color: fix alpha calculation < 16/255 and State-timeline threshold alpha * SpectrumPalette: Update to getColorByName * ColorValueEditor: Update to theme2 * SpectrumPalette: Update to theme2 * ColorPickerPopover: Format hex color * SpectrumPalette: Format hex color * SpectrumPalette: Refactor hex color alpha helper * SpectrumPalette: Fix import and update helper * ColorPickerPopover: Fix test to not make lowercase Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
be4b753aa0
commit
3e35021f7e
@ -32,7 +32,7 @@ import { getFrameDisplayName } from './fieldState';
|
|||||||
import { getTimeField } from '../dataframe/processDataFrame';
|
import { getTimeField } from '../dataframe/processDataFrame';
|
||||||
import { mapInternalLinkToExplore } from '../utils/dataLinks';
|
import { mapInternalLinkToExplore } from '../utils/dataLinks';
|
||||||
import { getTemplateProxyForField } from './templateProxies';
|
import { getTemplateProxyForField } from './templateProxies';
|
||||||
import tinycolor from 'tinycolor2';
|
import { asHexString } from '../themes/colorManipulator';
|
||||||
|
|
||||||
interface OverrideProps {
|
interface OverrideProps {
|
||||||
match: FieldMatcher;
|
match: FieldMatcher;
|
||||||
@ -227,8 +227,7 @@ function cachingDisplayProcessor(disp: DisplayProcessor, maxCacheSize = 2500): D
|
|||||||
// convert to hex6 or hex8 so downstream we can cheaply test for alpha (and set new alpha)
|
// convert to hex6 or hex8 so downstream we can cheaply test for alpha (and set new alpha)
|
||||||
// via a simple length check (in colorManipulator) rather using slow parsing via tinycolor
|
// via a simple length check (in colorManipulator) rather using slow parsing via tinycolor
|
||||||
if (v.color && v.color[0] !== '#') {
|
if (v.color && v.color[0] !== '#') {
|
||||||
let color = tinycolor(v.color);
|
v.color = asHexString(v.color);
|
||||||
v.color = color.getAlpha() < 1 ? color.toHex8String() : color.toHexString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.set(value, v);
|
cache.set(value, v);
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// https://github.com/mui-org/material-ui/blob/1b096070faf102281f8e3c4f9b2bf50acf91f412/packages/material-ui/src/styles/colorManipulator.js#L97
|
// https://github.com/mui-org/material-ui/blob/1b096070faf102281f8e3c4f9b2bf50acf91f412/packages/material-ui/src/styles/colorManipulator.js#L97
|
||||||
// MIT License Copyright (c) 2014 Call-Em-All
|
// MIT License Copyright (c) 2014 Call-Em-All
|
||||||
|
|
||||||
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a number whose value is limited to the given range.
|
* Returns a number whose value is limited to the given range.
|
||||||
* @param value The value to be clamped
|
* @param value The value to be clamped
|
||||||
@ -66,6 +68,19 @@ export function rgbToHex(color: string) {
|
|||||||
return `#${values.map((n: number) => intToHex(n)).join('')}`;
|
return `#${values.map((n: number) => intToHex(n)).join('')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a color to hex6 format if there is no alpha, hex8 if there is.
|
||||||
|
* @param color - Hex, RGB, HSL color
|
||||||
|
* @returns A hex color string, i.e. #ff0000 or #ff0000ff
|
||||||
|
*/
|
||||||
|
export function asHexString(color: string): string {
|
||||||
|
if (color[0] === '#') {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
const tColor = tinycolor(color);
|
||||||
|
return tColor.getAlpha() === 1 ? tColor.toHexString() : tColor.toHex8String();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a color from hsl format to rgb format.
|
* Converts a color from hsl format to rgb format.
|
||||||
* @param color - HSL color values
|
* @param color - HSL color values
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { mount, ReactWrapper } from 'enzyme';
|
import { mount, ReactWrapper } from 'enzyme';
|
||||||
import { ColorPickerPopover } from './ColorPickerPopover';
|
import { ColorPickerPopover } from './ColorPickerPopover';
|
||||||
import { ColorSwatch } from './ColorSwatch';
|
import { ColorSwatch } from './ColorSwatch';
|
||||||
import { createTheme, getColorForTheme } from '@grafana/data';
|
import { createTheme } from '@grafana/data';
|
||||||
|
|
||||||
describe('ColorPickerPopover', () => {
|
describe('ColorPickerPopover', () => {
|
||||||
const theme = createTheme();
|
const theme = createTheme();
|
||||||
@ -35,7 +35,7 @@ describe('ColorPickerPopover', () => {
|
|||||||
basicBlueSwatch.simulate('click');
|
basicBlueSwatch.simulate('click');
|
||||||
|
|
||||||
expect(onChangeSpy).toBeCalledTimes(1);
|
expect(onChangeSpy).toBeCalledTimes(1);
|
||||||
expect(onChangeSpy).toBeCalledWith(getColorForTheme('green', theme.v1));
|
expect(onChangeSpy).toBeCalledWith(theme.visualization.getColorByName('green'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass color name to onChange prop when named colors enabled', () => {
|
it('should pass color name to onChange prop when named colors enabled', () => {
|
||||||
|
@ -5,7 +5,7 @@ import SpectrumPalette from './SpectrumPalette';
|
|||||||
import { Themeable2 } from '../../types/theme';
|
import { Themeable2 } from '../../types/theme';
|
||||||
import { warnAboutColorPickerPropsDeprecation } from './warnAboutColorPickerPropsDeprecation';
|
import { warnAboutColorPickerPropsDeprecation } from './warnAboutColorPickerPropsDeprecation';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2, colorManipulator } from '@grafana/data';
|
||||||
import { stylesFactory, withTheme2 } from '../../themes';
|
import { stylesFactory, withTheme2 } from '../../themes';
|
||||||
|
|
||||||
export type ColorPickerChangeHandler = (color: string) => void;
|
export type ColorPickerChangeHandler = (color: string) => void;
|
||||||
@ -55,11 +55,10 @@ class UnThemedColorPickerPopover<T extends CustomPickersDescriptor> extends Reac
|
|||||||
handleChange = (color: any) => {
|
handleChange = (color: any) => {
|
||||||
const { onColorChange, onChange, enableNamedColors, theme } = this.props;
|
const { onColorChange, onChange, enableNamedColors, theme } = this.props;
|
||||||
const changeHandler = onColorChange || onChange;
|
const changeHandler = onColorChange || onChange;
|
||||||
|
|
||||||
if (enableNamedColors) {
|
if (enableNamedColors) {
|
||||||
return changeHandler(color);
|
return changeHandler(color);
|
||||||
}
|
}
|
||||||
changeHandler(theme.visualization.getColorByName(color));
|
changeHandler(colorManipulator.asHexString(theme.visualization.getColorByName(color)));
|
||||||
};
|
};
|
||||||
|
|
||||||
onTabChange = (tab: PickerType | keyof T) => {
|
onTabChange = (tab: PickerType | keyof T) => {
|
||||||
|
@ -3,9 +3,9 @@ import React, { useMemo, useState } from 'react';
|
|||||||
import { RgbaStringColorPicker } from 'react-colorful';
|
import { RgbaStringColorPicker } from 'react-colorful';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import ColorInput from './ColorInput';
|
import ColorInput from './ColorInput';
|
||||||
import { GrafanaTheme, getColorForTheme } from '@grafana/data';
|
import { GrafanaTheme2, colorManipulator } from '@grafana/data';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { useStyles, useTheme2 } from '../../themes';
|
import { useStyles2, useTheme2 } from '../../themes';
|
||||||
import { useThrottleFn } from 'react-use';
|
import { useThrottleFn } from 'react-use';
|
||||||
|
|
||||||
export interface SpectrumPaletteProps {
|
export interface SpectrumPaletteProps {
|
||||||
@ -15,16 +15,23 @@ export interface SpectrumPaletteProps {
|
|||||||
|
|
||||||
const SpectrumPalette: React.FunctionComponent<SpectrumPaletteProps> = ({ color, onChange }) => {
|
const SpectrumPalette: React.FunctionComponent<SpectrumPaletteProps> = ({ color, onChange }) => {
|
||||||
const [currentColor, setColor] = useState(color);
|
const [currentColor, setColor] = useState(color);
|
||||||
useThrottleFn(onChange, 500, [currentColor]);
|
|
||||||
|
useThrottleFn(
|
||||||
|
(c) => {
|
||||||
|
onChange(colorManipulator.asHexString(theme.visualization.getColorByName(c)));
|
||||||
|
},
|
||||||
|
500,
|
||||||
|
[currentColor]
|
||||||
|
);
|
||||||
|
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const styles = useStyles(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
const rgbaString = useMemo(() => {
|
const rgbaString = useMemo(() => {
|
||||||
return currentColor.startsWith('rgba')
|
return currentColor.startsWith('rgba')
|
||||||
? currentColor
|
? currentColor
|
||||||
: tinycolor(getColorForTheme(currentColor, theme.v1)).toRgbString();
|
: tinycolor(theme.visualization.getColorByName(color)).toRgbString();
|
||||||
}, [currentColor, theme]);
|
}, [currentColor, theme, color]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
@ -34,7 +41,7 @@ const SpectrumPalette: React.FunctionComponent<SpectrumPaletteProps> = ({ color,
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
wrapper: css`
|
wrapper: css`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
`,
|
`,
|
||||||
@ -45,24 +52,24 @@ const getStyles = (theme: GrafanaTheme) => ({
|
|||||||
|
|
||||||
.react-colorful {
|
.react-colorful {
|
||||||
&__saturation {
|
&__saturation {
|
||||||
border-radius: ${theme.border.radius.sm} ${theme.border.radius.sm} 0 0;
|
border-radius: ${theme.v1.border.radius.sm} ${theme.v1.border.radius.sm} 0 0;
|
||||||
}
|
}
|
||||||
&__alpha {
|
&__alpha {
|
||||||
border-radius: 0 0 ${theme.border.radius.sm} ${theme.border.radius.sm};
|
border-radius: 0 0 ${theme.v1.border.radius.sm} ${theme.v1.border.radius.sm};
|
||||||
}
|
}
|
||||||
&__alpha,
|
&__alpha,
|
||||||
&__hue {
|
&__hue {
|
||||||
height: ${theme.spacing.md};
|
height: ${theme.spacing(2)};
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
&__pointer {
|
&__pointer {
|
||||||
height: ${theme.spacing.md};
|
height: ${theme.spacing(2)};
|
||||||
width: ${theme.spacing.md};
|
width: ${theme.spacing(2)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
colorInput: css`
|
colorInput: css`
|
||||||
margin-top: ${theme.spacing.md};
|
margin-top: ${theme.spacing(2)};
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { getColorForTheme, GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { ColorPicker } from '../ColorPicker/ColorPicker';
|
import { ColorPicker } from '../ColorPicker/ColorPicker';
|
||||||
import { stylesFactory, useTheme } from '../../themes';
|
import { useTheme2, useStyles2 } from '../../themes';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { ColorSwatch } from '../ColorPicker/ColorSwatch';
|
import { ColorSwatch } from '../ColorPicker/ColorSwatch';
|
||||||
|
|
||||||
@ -17,8 +17,8 @@ export interface ColorValueEditorProps {
|
|||||||
* @alpha
|
* @alpha
|
||||||
* */
|
* */
|
||||||
export const ColorValueEditor: React.FC<ColorValueEditorProps> = ({ value, onChange }) => {
|
export const ColorValueEditor: React.FC<ColorValueEditorProps> = ({ value, onChange }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme2();
|
||||||
const styles = getStyles(theme);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColorPicker color={value ?? ''} onChange={onChange} enableNamedColors={true}>
|
<ColorPicker color={value ?? ''} onChange={onChange} enableNamedColors={true}>
|
||||||
@ -30,7 +30,7 @@ export const ColorValueEditor: React.FC<ColorValueEditorProps> = ({ value, onCha
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
onClick={showColorPicker}
|
onClick={showColorPicker}
|
||||||
onMouseLeave={hideColorPicker}
|
onMouseLeave={hideColorPicker}
|
||||||
color={value ? getColorForTheme(value, theme) : theme.colors.formInputBorder}
|
color={value ? theme.visualization.getColorByName(value) : theme.components.input.borderColor}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -40,23 +40,23 @@ export const ColorValueEditor: React.FC<ColorValueEditorProps> = ({ value, onCha
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
spot: css`
|
spot: css`
|
||||||
color: ${theme.colors.text};
|
color: ${theme.colors.text};
|
||||||
background: ${theme.colors.formInputBg};
|
background: ${theme.components.input.background};
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
height: ${theme.spacing.formInputHeight}px;
|
height: ${theme.v1.spacing.formInputHeight}px;
|
||||||
border: 1px solid ${theme.colors.formInputBorder};
|
border: 1px solid ${theme.components.input.borderColor};
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
&:hover {
|
&:hover {
|
||||||
border: 1px solid ${theme.colors.formInputBorderHover};
|
border: 1px solid ${theme.components.input.borderHover};
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
colorPicker: css`
|
colorPicker: css`
|
||||||
padding: 0 ${theme.spacing.sm};
|
padding: 0 ${theme.spacing(1)};
|
||||||
`,
|
`,
|
||||||
colorText: css`
|
colorText: css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -64,10 +64,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
`,
|
`,
|
||||||
trashIcon: css`
|
trashIcon: css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: ${theme.colors.textWeak};
|
color: ${theme.colors.text.secondary};
|
||||||
&:hover {
|
&:hover {
|
||||||
color: ${theme.colors.text};
|
color: ${theme.colors.text};
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user