Skip to content

Commit

Permalink
EuiColorPicker opacity support (#2850)
Browse files Browse the repository at this point in the history
* alpha range

* alpha tests

* docs

* wip: rgba support

* maintain alpha value

* prevent track visibility; clean up

* display toggles

* docs validation

* use rgba for swatches

* use rgba for css

* better hue and format throughput

* color stops validation

* more docs

* better format output

* disallow opacity when showAlpha=false

* clean up

* updated opacity and validity logic

* fix button example

* better format detection for null

* alpha channel for color stops

* allow empty range value

* sanitize rgb strings

* more resiliant sanitization

* feedback

* clean up

* usesEffect; clean up

* utils tests

* comment

* CL
  • Loading branch information
thompsongl authored Mar 12, 2020
1 parent 1457a41 commit cbbf27b
Show file tree
Hide file tree
Showing 27 changed files with 904 additions and 256 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Removed `role` attribute from `EuiImage`([#3036](https://github.com/elastic/eui/pull/3036))
- Added `prepend` and `append` ability to `EuiComboBox` single selection only ([#3003](https://github.com/elastic/eui/pull/3003))
- Added `onColumnResize` prop to `EuiDataGrid` of type `EuiDataGridOnColumnResizeHandler` that gets called when column changes it's size ([#2963](https://github.com/elastic/eui/pull/2963))
- Added RGB format support to `EuiColorPicker` and `EuiColorStops` ([#2850](https://github.com/elastic/eui/pull/2850))
- Added alpha channel (opacity) support to `EuiColorPicker` and `EuiColorStops` ([#2850](https://github.com/elastic/eui/pull/2850))

**Bug Fixes**

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"test-staged"
],
"dependencies": {
"@types/chroma-js": "^1.4.3",
"@types/chroma-js": "^2.0.0",
"@types/enzyme": "^3.1.13",
"@types/lodash": "^4.14.116",
"@types/numeral": "^0.0.25",
Expand Down
60 changes: 60 additions & 0 deletions src-docs/src/views/color_picker/alpha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';

import { EuiColorPicker, EuiFormRow } from '../../../../src/components';
import { useColorPicker } from './utils';

export const Alpha = () => {
const [color, setColor, errors] = useColorPicker('#D36086');
const [color2, setColor2, errors2] = useColorPicker('211, 96, 134');

const customSwatches = [
'#54B399',
'#6092C0',
'#D36086',
'#9170B8',
'#CA8EAE',
'#54B39940',
'#6092C040',
'#D3608640',
'#9170B840',
'#CA8EAE40',
];

const customSwatches2 = [
'211, 96, 134, 0.25',
'211, 96, 134, 0.5',
'211, 96, 134, 0.75',
'211, 96, 134',
];

return (
<>
<EuiFormRow
label="Pick a color with optional opacity"
isInvalid={!!errors}
error={errors}>
<EuiColorPicker
onChange={setColor}
color={color}
showAlpha={true}
isInvalid={!!errors}
swatches={customSwatches}
/>
</EuiFormRow>

<EuiFormRow
label="Using RGBa format"
isInvalid={!!errors2}
error={errors2}>
<EuiColorPicker
onChange={setColor2}
color={color2}
showAlpha={true}
format="rgba"
isInvalid={!!errors2}
swatches={customSwatches2}
/>
</EuiFormRow>
</>
);
};
45 changes: 11 additions & 34 deletions src-docs/src/views/color_picker/color_picker.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,13 @@
import React, { Component } from 'react';
import React from 'react';

import { EuiColorPicker, EuiFormRow } from '../../../../src/components';
import { isValidHex } from '../../../../src/services';

export class ColorPicker extends Component {
constructor(props) {
super(props);
this.state = {
color: '#D36086',
};
}

handleChange = value => {
this.setState({ color: value });
};

render() {
const hasErrors = !isValidHex(this.state.color) && this.state.color !== '';

let errors;
if (hasErrors) {
errors = ['Provide a valid hex value'];
}

return (
<EuiFormRow label="Pick a color" isInvalid={hasErrors} error={errors}>
<EuiColorPicker
onChange={this.handleChange}
color={this.state.color}
isInvalid={hasErrors}
/>
</EuiFormRow>
);
}
}
import { useColorPicker } from './utils';

export const ColorPicker = () => {
const [color, setColor, errors] = useColorPicker('#D36086');
return (
<EuiFormRow label="Pick a color" isInvalid={!!errors} error={errors}>
<EuiColorPicker onChange={setColor} color={color} isInvalid={!!errors} />
</EuiFormRow>
);
};
82 changes: 78 additions & 4 deletions src-docs/src/views/color_picker/color_picker_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,28 @@ const colorPickerRangeSnippet = `<EuiColorStops
/>
`;

import { Alpha } from './alpha';
const alphaSource = require('!!raw-loader!./alpha');
const alphaHtml = renderToHtml(Alpha);
const alphaSnippet = `<EuiColorPicker
id={colorPickerId}
onChange={handleChange}
color={chosenColor}
showAlpha={true}
isInvalid={hasErrors}
/>`;

import { Formats } from './formats';
const formatsSource = require('!!raw-loader!./formats');
const formatsHtml = renderToHtml(Formats);
const formatsSnippet = `<EuiColorPicker
format="hex"
id={colorPickerId}
onChange={handleChange}
color={chosenColor}
isInvalid={hasErrors}
/>`;

import { CustomSwatches } from './custom_swatches';
const customSwatchesSource = require('!!raw-loader!./custom_swatches');
const customSwatchesHtml = renderToHtml(CustomSwatches);
Expand Down Expand Up @@ -243,13 +265,13 @@ export const ColorPickerExample = {
selection.
</p>
<p>
Direct text entry will only match hexadecimal (hex) colors, and
output values only return hex values. Spatial selection involves
HSV manipulaton, which is converted to hex.
Direct text entry will match hexadecimal (hex) and RGB(a) colors,
and output will return both hex and RGBa values. Spatial selection
involves HSV manipulaton, which is converted to hex.
</p>
<p>
Swatches allow consumers to predefine preferred or suggested
choices. The swatches must also be entered in hex format.
choices. The swatches must also be entered in hex or RGBa format.
</p>
</EuiText>
</React.Fragment>
Expand Down Expand Up @@ -329,6 +351,58 @@ export const ColorPickerExample = {
snippet: colorPickerRangeSnippet,
demo: <ColorStopsRange />,
},
{
title: 'Format selection',
source: [
{
type: GuideSectionTypes.JS,
code: formatsSource,
},
{
type: GuideSectionTypes.HTML,
code: formatsHtml,
},
],
text: (
<>
<p>
Format selection does <em>not</em> limit the format of text input
the picker will allow, but instead attempts to keep consistency
during HSV selection. By default, the color picker will
automatically use the last input value format. Notice in following
the examples how hue and saturation selection behave differently.
</p>
<p>
Swatches will always show the &quot;as-authored&quot; color value,
as will the value provided via the <EuiCode>color</EuiCode> prop.
</p>
</>
),
snippet: formatsSnippet,
demo: <Formats />,
},
{
title: 'Alpha channel (opacity) selection',
source: [
{
type: GuideSectionTypes.JS,
code: alphaSource,
},
{
type: GuideSectionTypes.HTML,
code: alphaHtml,
},
],
text: (
<p>
To allow color opacity via alpha channel, set the{' '}
<EuiCode>showAlpha</EuiCode> prop to `true`. This will also display a
range slider allowing manual opacity updates.
</p>
),
snippet: alphaSnippet,
demo: <Alpha />,
},
{
title: 'Custom color swatches',
source: [
Expand Down
82 changes: 34 additions & 48 deletions src-docs/src/views/color_picker/custom_button.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component, Fragment } from 'react';
import React, { Fragment, useState } from 'react';

import {
EuiColorPicker,
Expand All @@ -8,56 +8,42 @@ import {
EuiSpacer,
} from '../../../../src/components';

import { isValidHex } from '../../../../src/services';
import { useColorPicker } from './utils';

export class CustomButton extends Component {
constructor(props) {
super(props);
this.state = {
color: null,
};
}

handleChange = value => {
this.setState({ color: value });
export const CustomButton = () => {
const [color, setColor, errors] = useColorPicker('');
const [selectedColor, setSelectedColor] = useState(color);
const handleColorChange = (text, { hex, isValid }) => {
setColor(text, { hex, isValid });
setSelectedColor(hex);
};

render() {
const hasErrors = !isValidHex(this.state.color) && this.state.color !== '';

let errors;
if (hasErrors) {
errors = ['Provide a valid hex value'];
}

return (
<Fragment>
<EuiFormRow label="Pick a color" error={errors}>
<EuiColorPicker
onChange={this.handleChange}
color={this.state.color}
button={
<EuiColorPickerSwatch
color={this.state.color}
aria-label="Select a new color"
/>
}
/>
</EuiFormRow>
<EuiSpacer />
return (
<Fragment>
<EuiFormRow label="Pick a color" error={errors}>
<EuiColorPicker
onChange={this.handleChange}
color={this.state.color}
isInvalid={hasErrors}
onChange={handleColorChange}
color={color}
button={
<EuiBadge
color={this.state.color ? this.state.color : 'hollow'}
onClickAriaLabel="Select a new color">
Color this badge
</EuiBadge>
<EuiColorPickerSwatch
color={selectedColor}
aria-label="Select a new color"
/>
}
/>
</Fragment>
);
}
}
</EuiFormRow>
<EuiSpacer />
<EuiColorPicker
onChange={handleColorChange}
color={color}
isInvalid={!!errors}
button={
<EuiBadge
color={selectedColor ? selectedColor : 'hollow'}
onClickAriaLabel="Select a new color">
Color this badge
</EuiBadge>
}
/>
</Fragment>
);
};
38 changes: 38 additions & 0 deletions src-docs/src/views/color_picker/formats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';

import { EuiColorPicker, EuiFormRow } from '../../../../src/components';
import { useColorPicker } from './utils';

export const Formats = () => {
const [color, setColor, errors] = useColorPicker('#D36086');
const [color2, setColor2, errors2] = useColorPicker('#D36086');
const [color3, setColor3, errors3] = useColorPicker('211, 96, 134');
return (
<>
<EuiFormRow label="Auto format" isInvalid={!!errors} error={errors}>
<EuiColorPicker
onChange={setColor}
color={color}
isInvalid={!!errors}
/>
</EuiFormRow>
<EuiFormRow label="Hex format" isInvalid={!!errors2} error={errors2}>
<EuiColorPicker
format="hex"
onChange={setColor2}
color={color2}
isInvalid={!!errors2}
/>
</EuiFormRow>
<EuiFormRow label="RGB(a) format" isInvalid={!!errors3} error={errors3}>
<EuiColorPicker
format="rgba"
onChange={setColor3}
color={color3}
isInvalid={!!errors3}
showAlpha={true}
/>
</EuiFormRow>
</>
);
};
Loading

0 comments on commit cbbf27b

Please sign in to comment.