Commit ac378790 by Dominik Prokop

WIP Basics of named color picker

parent 7ad430a6
import React, { FunctionComponent } from 'react';
import { storiesOf } from '@storybook/react';
import { ColorPicker } from '@grafana/ui';
import { withInfo } from '@storybook/addon-info';
import { ColorPickerPopover } from './ColorPickerPopover';
const CenteredStory: FunctionComponent<{}> = ({ children }) => {
return (
......@@ -20,6 +19,9 @@ const CenteredStory: FunctionComponent<{}> = ({ children }) => {
storiesOf('UI/ColorPicker', module)
.addDecorator(story => <CenteredStory>{story()}</CenteredStory>)
.add('default', withInfo({inline: true})(() => {
return <ColorPicker color="#ff0000" onChange={() => {}} />;
}));
// .add('Color picker popover', () => {
// return <ColorPickerPopover color="#ff0000" onColorSelect={() => {}} />;
// })
.add('Named colors swatch', () => {
return <ColorPickerPopover color="#ff0000" onColorSelect={() => {}} />;
});
......@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Drop from 'tether-drop';
import { ColorPickerPopover } from './ColorPickerPopover';
import { Color } from '../../utils/colorsPalette';
interface Props {
/**
......@@ -9,6 +10,7 @@ interface Props {
*
* @default " "
**/
name?: Color;
color: string;
onChange: (c: string) => void;
}
......@@ -55,6 +57,7 @@ export class ColorPicker extends Component<Props, any> {
};
render() {
return (
<div className="sp-replacer sp-light" onClick={this.openColorPicker} ref={element => (this.pickerElem = element)}>
<div className="sp-preview">
......
import React, { FunctionComponent } from 'react';
import { storiesOf } from '@storybook/react';
import NamedColorsPicker from './NamedColorsPicker';
import { Color, getColorName } from '@grafana/ui/src/utils/colorsPalette';
const CenteredStory: FunctionComponent<{}> = ({ children }) => {
return (
<div
style={{
height: '100vh ',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{children}
</div>
);
};
interface StateHolderProps<T> {
initialState: T;
children: (currentState: T, updateState: (nextState: T) => void) => JSX.Element;
}
class UseState<T> extends React.Component<StateHolderProps<T>, {value: T}> {
constructor(props: StateHolderProps<T>) {
super(props)
this.state = {
value: props.initialState
}
}
handleStateUpdate = (nextState: T) => {
this.setState({value: nextState})
}
render() {
return this.props.children(this.state.value, this.handleStateUpdate)
}
}
storiesOf('UI/ColorPicker', module)
.addDecorator(story => <CenteredStory>{story()}</CenteredStory>)
.add('Named colors swatch - support for named colors', () => {
return(
<UseState initialState="green">
{(selectedColor, updateSelectedColor) => {
return (
<NamedColorsPicker
selectedColor={selectedColor as Color}
onChange={(color) => { updateSelectedColor(color.name);}}
/>
)
}}
</UseState>);
})
.add('Named colors swatch - support for hex values', () => {
return(
<UseState initialState="#00ff00">
{(selectedColor, updateSelectedColor) => {
return (
<NamedColorsPicker
selectedColor={getColorName(selectedColor)}
onChange={(color) => updateSelectedColor(color.variants.dark)}
/>
)
}}
</UseState>);
});
import React, { FunctionComponent } from 'react';
import { find, upperFirst } from 'lodash';
import { Color, ColorsPalete, ColorDefinition } from '../../utils/colorsPalette';
type ColorChangeHandler = (color: ColorDefinition) => void;
enum ColorSwatchVariant {
Small = 'small',
Large = 'large',
}
interface ColorSwatchProps extends React.DOMAttributes<HTMLDivElement> {
color: ColorDefinition;
variant?: ColorSwatchVariant;
isSelected?: boolean;
}
const ColorSwatch: FunctionComponent<ColorSwatchProps> = ({
color,
variant = ColorSwatchVariant.Small,
isSelected,
...otherProps
}) => {
const isSmall = variant === ColorSwatchVariant.Small;
const swatchSize = isSmall ? '16px' : '32px';
const swatchStyles = {
width: swatchSize,
height: swatchSize,
borderRadius: '50%',
background: `${color.variants.dark}`,
marginRight: isSmall ? '0px' : '8px',
boxShadow: isSelected ? `inset 0 0 0 2px ${color.variants.dark}, inset 0 0 0 4px white` : 'none',
cursor: isSelected ? 'default' : 'pointer'
};
return (
<div
style={{
display: 'flex',
alignItems: 'center',
}}
{...otherProps}
>
<div style={swatchStyles} />
{variant === ColorSwatchVariant.Large && <span>{upperFirst(color.hue)}</span>}
</div>
);
};
const ColorsGroup = ({
colors,
selectedColor,
onColorSelect,
}: {
colors: ColorDefinition[];
selectedColor?: Color;
onColorSelect: ColorChangeHandler
}) => {
const primaryColor = find(colors, color => !!color.isPrimary);
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
{primaryColor && (
<ColorSwatch
isSelected={primaryColor.name === selectedColor}
variant={ColorSwatchVariant.Large}
color={primaryColor}
onClick={() => onColorSelect(primaryColor)}
/>
)}
<div
style={{
display: 'flex',
marginTop: '8px',
}}
>
{colors.map(color => !color.isPrimary && (
<div style={{ marginRight: '4px' }}>
<ColorSwatch
isSelected={color.name === selectedColor}
color={color}
onClick={() => onColorSelect(color)}
/>
</div>
))}
</div>
</div>
);
};
interface NamedColorsPickerProps {
selectedColor?: Color;
onChange: ColorChangeHandler;
}
const NamedColorsPicker = ({ selectedColor, onChange }: NamedColorsPickerProps) => {
const swatches: JSX.Element[] = [];
ColorsPalete.forEach((colors, hue) => {
swatches.push(
<>
<ColorsGroup selectedColor={selectedColor} colors={colors} onColorSelect={onChange} />
</>
);
});
return (
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gridRowGap: '32px',
gridColumnGap: '32px',
}}
>
{swatches}
</div>
);
};
export default NamedColorsPicker;
......@@ -9,7 +9,6 @@ export const ALERTING_COLOR = 'rgba(237, 46, 24, 1)';
export const NO_DATA_COLOR = 'rgba(150, 150, 150, 1)';
export const PENDING_COLOR = 'rgba(247, 149, 32, 1)';
export const REGION_FILL_ALPHA = 0.09;
export const colors = [
'#7EB26D', // 0: pale green
'#EAB839', // 1: mustard
......
import { getColorName, getColorDefinition, ColorsPalete, buildColorDefinition } from './colorsPalette';
describe('colors', () => {
const FakeBlue = buildColorDefinition('blue', 'blue', ['#0000ff', '#00000ee']);
beforeAll(() => {
ColorsPalete.set('blue', [FakeBlue])
});
describe('getColorDefinition', () => {
it('returns undefined for unknown hex', () => {
expect(getColorDefinition('#ff0000')).toBeUndefined();
});
it('returns definition for known hex', () => {
expect(getColorDefinition(FakeBlue.variants.light)).toEqual(FakeBlue);
expect(getColorDefinition(FakeBlue.variants.dark)).toEqual(FakeBlue);
});
});
describe('getColorName', () => {
it('returns undefined for unknown hex', () => {
expect(getColorName('#ff0000')).toBeUndefined();
});
it('returns name for known hex', () => {
expect(getColorName(FakeBlue.variants.light)).toEqual(FakeBlue.name);
expect(getColorName(FakeBlue.variants.dark)).toEqual(FakeBlue.name);
});
});
});
import { flatten, some, values } from 'lodash';
type Hue = 'green' | 'yellow' | 'red' | 'blue' | 'orange' | 'purple';
export type Color =
| 'green'
| 'dark-green'
| 'semi-dark-green'
| 'light-green'
| 'super-light-green'
| 'yellow'
| 'dark-yellow'
| 'semi-dark-yellow'
| 'light-yellow'
| 'super-light-yellow'
| 'red'
| 'dark-red'
| 'semi-dark-red'
| 'light-red'
| 'super-light-red'
| 'blue'
| 'dark-blue'
| 'semi-dark-blue'
| 'light-blue'
| 'super-light-blue'
| 'orange'
| 'dark-orange'
| 'semi-dark-orange'
| 'light-orange'
| 'super-light-orange'
| 'purple'
| 'dark-purple'
| 'semi-dark-purple'
| 'light-purple'
| 'super-light-purple';
type ThemeVariants = {
dark: string;
light: string;
};
export type ColorDefinition = {
hue: Hue;
isPrimary?: boolean;
name: Color;
variants: ThemeVariants;
};
export const ColorsPalete = new Map<Hue, ColorDefinition[]>();
export const buildColorDefinition = (
hue: Hue,
name: Color,
[light, dark]: string[],
isPrimary?: boolean
): ColorDefinition => ({
hue,
name,
variants: {
light,
dark,
},
isPrimary: !!isPrimary,
});
export const BasicGreen = buildColorDefinition('green', 'green', ['#53A642', '#53A642'], true);
export const DarkGreen = buildColorDefinition('green', 'dark-green', ['#106100', '#106100']);
export const SemiDarkGreen = buildColorDefinition('green', 'semi-dark-green', ['#388729', '#388729']);
export const LightGreen = buildColorDefinition('green', 'light-green', ['#72C462', '#72C462']);
export const SuperLightGreen = buildColorDefinition('green', 'super-light-green', ['#99E68A', '#99E68A']);
export const BasicYellow = buildColorDefinition('yellow', 'yellow', ['#F2CA00', '#F2CA00'], true);
export const DarkYellow = buildColorDefinition('yellow', 'dark-yellow', ['#A68A00', '#A68A00']);
export const SemiDarkYellow = buildColorDefinition('yellow', 'semi-dark-yellow', ['#CCAA00', '#CCAA00']);
export const LightYellow = buildColorDefinition('yellow', 'light-yellow', ['#F7D636', '#F7D636']);
export const SuperLightYellow = buildColorDefinition('yellow', 'super-light-yellow', ['#FFEB8A', '#FFEB8A']);
export const BasicRed = buildColorDefinition('red', 'red', ['#F94462', '#F94462'], true);
export const DarkRed = buildColorDefinition('red', 'dark-red', ['#B3001D', '#B3001D']);
export const SemiDarkRed = buildColorDefinition('red', 'semi-dark-red', ['#D93651', '#D93651']);
export const LightRed = buildColorDefinition('red', 'light-red', ['#F07387', '#F07387']);
export const SuperLightRed = buildColorDefinition('red', 'super-light-red', ['#E6A1AC', '#E6A1AC']);
export const BasicBlue = buildColorDefinition('blue', 'blue', ['#408BFF', '#408BFF'], true);
export const DarkBlue = buildColorDefinition('blue', 'dark-blue', ['#2155A6', '#2155A6']);
export const SemiDarkBlue = buildColorDefinition('blue', 'semi-dark-blue', ['#3471CF', '#3471CF']);
export const LightBlue = buildColorDefinition('blue', 'light-blue', ['#7DAEFA', '#7DAEFA']);
export const SuperLightBlue = buildColorDefinition('blue', 'super-light-blue', ['#B8D0F5', '#B8D0F5']);
export const BasicOrange = buildColorDefinition('orange', 'orange', ['#FA6400', '#FA6400'], true);
export const DarkOrange = buildColorDefinition('orange', 'dark-orange', ['#963C00', '#963C00']);
export const SemiDarkOrange = buildColorDefinition('orange', 'semi-dark-orange', ['#ED4B00', '#ED4B00']);
export const LightOrange = buildColorDefinition('orange', 'light-orange', ['#FC934C', '#FC934C']);
export const SuperLightOrange = buildColorDefinition('orange', 'super-light-orange', ['#FFC299', '#FFC299']);
export const BasicPurple = buildColorDefinition('purple', 'purple', ['#BC67E6', '#BC67E6'], true);
export const DarkPurple = buildColorDefinition('purple', 'dark-purple', ['#701F99', '#701F99']);
export const SemiDarkPurple = buildColorDefinition('purple', 'semi-dark-purple', ['#9E43CC', '#9E43CC']);
export const LightPurple = buildColorDefinition('purple', 'light-purple', ['#D19AED', '#D19AED']);
export const SuperLightPurple = buildColorDefinition('purple', 'super-light-purple', ['#E6CEF2', '#E6CEF2']);
const greens = [BasicGreen, DarkGreen, SemiDarkGreen, LightGreen, SuperLightGreen];
const yellows = [BasicYellow, DarkYellow, SemiDarkYellow, LightYellow, SuperLightYellow];
const reds = [BasicRed, DarkRed, SemiDarkRed, LightRed, SuperLightRed];
const blues = [BasicBlue, DarkBlue, SemiDarkBlue, LightBlue, SuperLightBlue];
const oranges = [BasicOrange, DarkOrange, SemiDarkOrange, LightOrange, SuperLightOrange];
const purples = [BasicPurple, DarkPurple, SemiDarkPurple, LightPurple, SuperLightPurple];
ColorsPalete.set('green', greens);
ColorsPalete.set('yellow', yellows);
ColorsPalete.set('red', reds);
ColorsPalete.set('blue', blues);
ColorsPalete.set('orange', oranges);
ColorsPalete.set('purple', purples);
export const getColorDefinition = (hex: string): ColorDefinition | undefined => {
return flatten(Array.from(ColorsPalete.values())).filter(definition =>
some(values(definition.variants), color => color === hex)
)[0];
};
export const getColorName = (hex: string): Color | undefined => {
const definition = getColorDefinition(hex);
return definition ? definition.name : undefined;
};
......@@ -1673,7 +1673,7 @@
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-1.10.35.tgz#4e5c2b1e5b3bf0b863efb8c5e70081f52e6c9518"
integrity sha512-SVtqEcudm7yjkTwoRA1gC6CNMhGDdMx4Pg8BPdiqI7bXXdCn1BPmtxgeWYQOgDxrq53/5YTlhq5ULxBEAlWIBg==
"@types/lodash@4.14.119", "@types/lodash@^4.14.119":
"@types/lodash@^4.14.119":
version "4.14.119"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39"
integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw==
......@@ -1735,7 +1735,7 @@
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^16.7.6":
"@types/react@*", "@types/react@16.7.6", "@types/react@^16.7.6":
version "16.7.6"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040"
integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA==
......@@ -4429,7 +4429,7 @@ caniuse-api@^1.5.2:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
version "1.0.30000772"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b"
integrity sha1-UarokXaChureSj2DGep21qAbUSs=
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment