[canvas] Color Widgets - TS + Examples (#31391)

* [canvas] Color Widgets - TS + Examples

* Addressing feedback
This commit is contained in:
Clint Andrew Hall 2019-03-13 23:09:05 -05:00 committed by GitHub
parent 4309a8b4fd
commit a807511d59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 4018 additions and 178 deletions

View file

@ -111,6 +111,7 @@
"@kbn/ui-framework": "1.0.0",
"@types/json-stable-stringify": "^1.0.32",
"@types/lodash.clonedeep": "^4.5.4",
"@types/recompose": "^0.30.5",
"JSONStream": "1.1.1",
"abortcontroller-polyfill": "^1.1.9",
"angular": "1.6.9",

View file

@ -38,6 +38,8 @@
"@storybook/react": "^4.0.7",
"@types/angular": "1.6.50",
"@types/cheerio": "^0.22.10",
"@types/chroma-js": "^1.4.1",
"@types/color": "^3.0.0",
"@types/d3-array": "^1.2.1",
"@types/d3-scale": "^2.0.0",
"@types/d3-shape": "^1.3.1",
@ -65,6 +67,7 @@
"@types/storybook__addon-info": "^3.4.2",
"@types/storybook__react": "^4.0.0",
"@types/supertest": "^2.0.5",
"@types/tinycolor2": "^1.4.1",
"@types/uuid": "^3.4.4",
"abab": "^1.0.4",
"ansi-colors": "^3.0.5",

View file

@ -0,0 +1,209 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Storyshots components/ColorDot color dots 1`] = `
Array [
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(255, 255, 255)",
}
}
/>
</div>,
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(100, 150, 250)",
}
}
/>
</div>,
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgba(100, 150, 250, 0.5)",
}
}
/>
</div>,
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(0, 0, 0)",
}
}
/>
</div>,
]
`;
exports[`Storyshots components/ColorDot color dots with children 1`] = `
Array [
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(255, 255, 255)",
}
}
>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={
Object {
"fill": "#000",
}
}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</div>
</div>,
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(102, 102, 102)",
}
}
>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={
Object {
"fill": "#fff",
}
}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</div>
</div>,
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgba(100, 150, 250, 0.5)",
}
}
>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={
Object {
"fill": "#fff",
}
}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.378 1.496l6.695 10.984A1 1 0 0 1 14.22 14H1.667a1 1 0 0 1-.883-1.47L6.642 1.545a1 1 0 0 1 1.736-.05zm-.853.52L1.667 13h12.552L7.525 2.016zM7.14 10.06L6.9 5.18h1.3l-.25 4.878h-.81zm.394 1.901a.61.61 0 0 1-.448-.186.606.606 0 0 1-.186-.444c0-.174.062-.323.186-.446a.614.614 0 0 1 .448-.184c.169 0 .315.06.44.182.124.122.186.27.186.448a.6.6 0 0 1-.189.446.607.607 0 0 1-.437.184z"
fillRule="evenodd"
/>
</svg>
</div>
</div>,
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(0, 0, 0)",
}
}
>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={
Object {
"fill": "#fff",
}
}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.5 12a.502.502 0 0 1-.354-.146l-4-4a.502.502 0 0 1 .708-.708L6.5 10.793l6.646-6.647a.502.502 0 0 1 .708.708l-7 7A.502.502 0 0 1 6.5 12"
fillRule="evenodd"
/>
</svg>
</div>
</div>,
]
`;
exports[`Storyshots components/ColorDot invalid dots 1`] = `null`;

View file

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiIcon } from '@elastic/eui';
import { storiesOf } from '@storybook/react';
import React from 'react';
import { ColorDot } from '../color_dot';
storiesOf('components/ColorDot', module)
.addParameters({ info: { propTablesExclude: [EuiIcon] } })
.add('color dots', () => [
<ColorDot key="1" value="white" />,
<ColorDot key="2" value="rgb(100, 150, 250)" />,
<ColorDot key="3" value="rgba(100, 150, 250, .5)" />,
<ColorDot key="4" value="#000" />,
])
.add('invalid dots', () => [
<ColorDot key="1" value="elastic" />,
<ColorDot key="2" value="#canvas" />,
<ColorDot key="3" value="#abcd" />,
])
.add('color dots with children', () => [
<ColorDot key="1" value="#FFF">
<EuiIcon type="plusInCircle" color="#000" />
</ColorDot>,
<ColorDot key="2" value="#666">
<EuiIcon type="minusInCircle" color="#fff" />
</ColorDot>,
<ColorDot key="3" value="rgba(100, 150, 250, .5)">
<EuiIcon type="alert" color="#fff" />
</ColorDot>,
<ColorDot key="4" value="#000">
<EuiIcon type="check" color="#fff" />
</ColorDot>,
]);

View file

@ -4,14 +4,27 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import React, { ReactNode, SFC } from 'react';
import tinycolor from 'tinycolor2';
export interface Props {
/** Any valid CSS color. If not a valid CSS string, the dot will not render */
value: string;
/** Nodes to display within the dot. Should fit within the constraints. */
children?: ReactNode;
}
export const ColorDot: SFC<Props> = ({ value, children }) => {
const tc = tinycolor(value);
if (!tc.isValid()) {
return null;
}
export const ColorDot = ({ value, children }) => {
return (
<div className="canvasColorDot">
<div className="canvasColorDot__background canvasCheckered" />
<div className="canvasColorDot__foreground" style={{ background: value }}>
<div className="canvasColorDot__foreground" style={{ background: tc.toRgbString() }}>
{children}
</div>
</div>
@ -21,5 +34,4 @@ export const ColorDot = ({ value, children }) => {
ColorDot.propTypes = {
value: PropTypes.string,
children: PropTypes.node,
handleClick: PropTypes.func,
};

View file

@ -0,0 +1,795 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Storyshots components/ColorManager default 1`] = `
Array [
<div
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(171, 205, 239)",
}
}
/>
</div>
</div>
<div
className="euiFlexItem"
style={
Object {
"display": "inline-block",
}
}
>
<div
className="euiFormControlLayout"
>
<div
className="euiFormControlLayout__childrenWrapper"
>
<input
className="euiFieldText"
onChange={[Function]}
placeholder="#hex color"
type="text"
value="#abcdef"
/>
</div>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<button
aria-label="Add Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</button>
<button
aria-label="Remove Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</button>
</div>
</div>,
<div
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(170, 187, 204)",
}
}
/>
</div>
</div>
<div
className="euiFlexItem"
style={
Object {
"display": "inline-block",
}
}
>
<div
className="euiFormControlLayout"
>
<div
className="euiFormControlLayout__childrenWrapper"
>
<input
className="euiFieldText"
onChange={[Function]}
placeholder="#hex color"
type="text"
value="#abc"
/>
</div>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<button
aria-label="Add Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</button>
<button
aria-label="Remove Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</button>
</div>
</div>,
]
`;
exports[`Storyshots components/ColorManager interactive 1`] = `
<div
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgba(255, 255, 255, 0)",
}
}
/>
</div>
</div>
<div
className="euiFlexItem"
style={
Object {
"display": "inline-block",
}
}
>
<div
className="euiFormControlLayout"
>
<div
className="euiFormControlLayout__childrenWrapper"
>
<input
className="euiFieldText"
onChange={[Function]}
placeholder="#hex color"
type="text"
value=""
/>
</div>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<button
aria-label="Add Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</button>
<button
aria-label="Remove Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</button>
</div>
</div>
`;
exports[`Storyshots components/ColorManager invalid colors 1`] = `
Array [
<div
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgba(255, 255, 255, 0)",
}
}
/>
</div>
</div>
<div
className="euiFlexItem"
style={
Object {
"display": "inline-block",
}
}
>
<div
className="euiFormControlLayout"
>
<div
className="euiFormControlLayout__childrenWrapper"
>
<input
className="euiFieldText"
onChange={[Function]}
placeholder="#hex color"
type="text"
value="#abcd"
/>
</div>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<button
aria-label="Add Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</button>
<button
aria-label="Remove Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</button>
</div>
</div>,
<div
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgba(255, 255, 255, 0)",
}
}
/>
</div>
</div>
<div
className="euiFlexItem"
style={
Object {
"display": "inline-block",
}
}
>
<div
className="euiFormControlLayout"
>
<div
className="euiFormControlLayout__childrenWrapper"
>
<input
className="euiFieldText"
onChange={[Function]}
placeholder="#hex color"
type="text"
value="#canvas"
/>
</div>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<button
aria-label="Add Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</button>
<button
aria-label="Remove Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</button>
</div>
</div>,
]
`;
exports[`Storyshots components/ColorManager with buttons 1`] = `
Array [
<div
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(171, 205, 239)",
}
}
/>
</div>
</div>
<div
className="euiFlexItem"
style={
Object {
"display": "inline-block",
}
}
>
<div
className="euiFormControlLayout"
>
<div
className="euiFormControlLayout__childrenWrapper"
>
<input
className="euiFieldText"
onChange={[Function]}
placeholder="#hex color"
type="text"
value="#abcdef"
/>
</div>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<button
aria-label="Add Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</button>
<button
aria-label="Remove Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</button>
</div>
</div>,
<div
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(171, 205, 239)",
}
}
/>
</div>
</div>
<div
className="euiFlexItem"
style={
Object {
"display": "inline-block",
}
}
>
<div
className="euiFormControlLayout"
>
<div
className="euiFormControlLayout__childrenWrapper"
>
<input
className="euiFieldText"
onChange={[Function]}
placeholder="#hex color"
type="text"
value="#abcdef"
/>
</div>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<button
aria-label="Add Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={true}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</button>
<button
aria-label="Remove Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</button>
</div>
</div>,
<div
className="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(171, 205, 239)",
}
}
/>
</div>
</div>
<div
className="euiFlexItem"
style={
Object {
"display": "inline-block",
}
}
>
<div
className="euiFormControlLayout"
>
<div
className="euiFormControlLayout__childrenWrapper"
>
<input
className="euiFieldText"
onChange={[Function]}
placeholder="#hex color"
type="text"
value="#abcdef"
/>
</div>
</div>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<button
aria-label="Add Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</button>
<button
aria-label="Remove Color"
className="euiButtonIcon euiButtonIcon--primary"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="euiIcon euiIcon--medium euiButtonIcon__icon"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</button>
</div>
</div>,
]
`;

View file

@ -0,0 +1,88 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';
import React from 'react';
import { ColorManager } from '../color_manager';
class Interactive extends React.Component<{}, { value: string }> {
public state = {
value: '',
};
public render() {
return (
<ColorManager
onAddColor={action('onAddColor')}
onRemoveColor={action('onRemoveColor')}
onChange={value => this.setState({ value })}
value={this.state.value}
/>
);
}
}
storiesOf('components/ColorManager', module)
.addParameters({
info: {
inline: true,
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '40px 60px',
width: '320px',
},
},
},
})
.add('default', () => [
<ColorManager key="1" onChange={action('onChange')} value="#abcdef" />,
<ColorManager key="2" onChange={action('onChange')} value="#abc" />,
])
.add('invalid colors', () => [
<ColorManager key="1" onChange={action('onChange')} value="#abcd" />,
<ColorManager key="2" onChange={action('onChange')} value="canvas" />,
])
.add('with buttons', () => [
<ColorManager
key="1"
onAddColor={action('onAddColor')}
onChange={action('onChange')}
value="#abcdef"
/>,
<ColorManager
key="2"
onChange={action('onChange')}
onRemoveColor={action('onRemoveColor')}
value="#abcdef"
/>,
<ColorManager
key="3"
onAddColor={action('onAddColor')}
onChange={action('onChange')}
onRemoveColor={action('onRemoveColor')}
value="#abcdef"
/>,
])
.add('interactive', () => <Interactive />, {
info: {
inline: true,
source: false,
propTablesExclude: [Interactive],
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '40px 60px',
width: '320px',
},
},
},
});

View file

@ -1,51 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { EuiFieldText, EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { ColorDot } from '../color_dot/color_dot';
export const ColorManager = ({ value, addColor, removeColor, onChange }) => (
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem grow={1}>
<ColorDot value={value} />
</EuiFlexItem>
<EuiFlexItem grow={5} style={{ display: 'inline-block' }}>
<EuiFieldText
compressed
value={value || ''}
placeholder="#hex color"
onChange={e => onChange(e.target.value)}
/>
</EuiFlexItem>
{(addColor || removeColor) && (
<EuiFlexItem grow={false}>
{addColor && (
<EuiButtonIcon
aria-label="Add Color"
iconType="plusInCircle"
onClick={() => addColor(value)}
/>
)}
{removeColor && (
<EuiButtonIcon
aria-label="Remove Color"
iconType="minusInCircle"
onClick={() => removeColor(value)}
/>
)}
</EuiFlexItem>
)}
</EuiFlexGroup>
);
ColorManager.propTypes = {
value: PropTypes.string,
addColor: PropTypes.func,
removeColor: PropTypes.func,
onChange: PropTypes.func.isRequired,
};

View file

@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiButtonIcon, EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import PropTypes from 'prop-types';
import React, { SFC } from 'react';
import tinycolor from 'tinycolor2';
import { ColorDot } from '../color_dot/color_dot';
export interface Props {
/** The function to call when the Add Color button is clicked. The button will not appear if there is no handler. */
onAddColor?: (value: string) => void;
/** The function to call when the value is changed */
onChange: (value: string) => void;
/** The function to call when the Remove Color button is clicked. The button will not appear if there is no handler. */
onRemoveColor?: (value: string) => void;
/**
* The value of the color manager. Only honors hexadecimal values.
* @default ''
*/
value?: string;
}
export const ColorManager: SFC<Props> = ({ value = '', onAddColor, onRemoveColor, onChange }) => {
const tc = tinycolor(value);
const validColor = tc.isValid() && tc.getFormat() === 'hex';
if (value.length > 0 && !value.startsWith('#')) {
value = '#' + value;
}
const add = (
<EuiButtonIcon
aria-label="Add Color"
iconType="plusInCircle"
isDisabled={!validColor || !onAddColor}
onClick={() => onAddColor && onAddColor(value)}
/>
);
const remove = (
<EuiButtonIcon
aria-label="Remove Color"
iconType="minusInCircle"
isDisabled={!validColor || !onRemoveColor}
onClick={() => onRemoveColor && onRemoveColor(value)}
/>
);
return (
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
<ColorDot value={validColor ? value : 'rgba(255,255,255,0)'} />
</EuiFlexItem>
<EuiFlexItem style={{ display: 'inline-block' }}>
<EuiFieldText
value={value}
isInvalid={!validColor && value.length > 0}
placeholder="#hex color"
onChange={e => onChange(e.target.value)}
/>
</EuiFlexItem>
{(add || remove) && (
<EuiFlexItem grow={false}>
{add}
{remove}
</EuiFlexItem>
)}
</EuiFlexGroup>
);
};
ColorManager.propTypes = {
onAddColor: PropTypes.func,
onChange: PropTypes.func.isRequired,
onRemoveColor: PropTypes.func,
value: PropTypes.string,
};

View file

@ -5,6 +5,7 @@
*/
import { pure } from 'recompose';
import { ItemGrid as Component } from './item_grid';
export const ItemGrid = pure(Component);
import { ColorManager as Component } from './color_manager';
export const ColorManager = pure(Component);

View file

@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';
import React from 'react';
import { ColorPalette } from '../color_palette';
const THREE_COLORS = ['#fff', '#666', '#000'];
const SIX_COLORS = ['#fff', '#666', '#000', '#abc', '#def', '#abcdef'];
class Interactive extends React.Component<{}, { value: string }> {
public state = {
value: '',
};
public render() {
return (
<ColorPalette
colors={SIX_COLORS}
onChange={value => this.setState({ value })}
value={this.state.value}
/>
);
}
}
storiesOf('components/ColorPalette', module)
.add('three colors', () => [
<ColorPalette key="1" onChange={action('onChange')} colors={THREE_COLORS} />,
<ColorPalette key="2" value="#fff" onChange={action('onChange')} colors={THREE_COLORS} />,
])
.add('six colors', () => [
<ColorPalette key="1" onChange={action('onChange')} colors={SIX_COLORS} />,
<ColorPalette key="2" value="#fff" onChange={action('onChange')} colors={SIX_COLORS} />,
])
.add('six colors, wrap at 4', () => (
<ColorPalette value="#fff" onChange={action('onChange')} colors={SIX_COLORS} colorsPerRow={4} />
))
.add('six colors, value missing', () => (
<ColorPalette value="#f00" onChange={action('onChange')} colors={SIX_COLORS} />
))
.add('interactive', () => <Interactive />, {
info: {
inline: true,
source: false,
propTablesExclude: [Interactive],
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '40px 60px',
},
},
},
});

View file

@ -1,40 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { EuiIcon, EuiLink } from '@elastic/eui';
import { readableColor } from '../../lib/readable_color';
import { ColorDot } from '../color_dot';
import { ItemGrid } from '../item_grid';
export const ColorPalette = ({ value, colors, colorsPerRow, onChange }) => (
<div className="canvasColorPalette">
<ItemGrid items={colors} itemsPerRow={colorsPerRow || 6}>
{({ item: color }) => (
<EuiLink
style={{ fontSize: 0 }}
key={color}
onClick={() => onChange(color)}
className="canvasColorPalette__dot"
>
<ColorDot value={color}>
{color === value && (
<EuiIcon type="check" className="selected-color" color={readableColor(value)} />
)}
</ColorDot>
</EuiLink>
)}
</ItemGrid>
</div>
);
ColorPalette.propTypes = {
colors: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
value: PropTypes.string,
colorsPerRow: PropTypes.number,
};

View file

@ -0,0 +1,80 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiIcon, EuiLink } from '@elastic/eui';
import PropTypes from 'prop-types';
import React, { SFC } from 'react';
import tinycolor from 'tinycolor2';
import { readableColor } from '../../lib/readable_color';
import { ColorDot } from '../color_dot';
import { ItemGrid } from '../item_grid';
export interface Props {
/**
* An array of hexadecimal color values. Non-hex will be ignored.
* @default []
*/
colors?: string[];
/**
* The number of colors to display before wrapping to a new row.
* @default 6
*/
colorsPerRow?: number;
/** The function to call when the color is changed. */
onChange: (value: string) => void;
/**
* The value of the color in the selector. Should be hexadecimal. If it is not in the colors array, it will be ignored.
* @default ''
*/
value?: string;
}
export const ColorPalette: SFC<Props> = ({
colors = [],
colorsPerRow = 6,
onChange,
value = '',
}) => {
if (colors.length === 0) {
return null;
}
colors = colors.filter(color => {
const providedColor = tinycolor(color);
return providedColor.isValid() && providedColor.getFormat() === 'hex';
});
return (
<div className="canvasColorPalette">
<ItemGrid items={colors} itemsPerRow={colorsPerRow}>
{color => {
const match = tinycolor.equals(color, value);
const icon = match ? (
<EuiIcon type="check" className="selected-color" color={readableColor(value)} />
) : null;
return (
<EuiLink
style={{ fontSize: 0 }}
key={color}
onClick={() => !match && onChange(color)}
className="canvasColorPalette__dot"
>
<ColorDot value={color}>{icon}</ColorDot>
</EuiLink>
);
}}
</ItemGrid>
</div>
);
};
ColorPalette.propTypes = {
colors: PropTypes.array,
colorsPerRow: PropTypes.number,
onChange: PropTypes.func.isRequired,
value: PropTypes.string,
};

View file

@ -0,0 +1,93 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';
import React from 'react';
import { ColorPicker } from '../color_picker';
const THREE_COLORS = ['#fff', '#666', '#000'];
const SIX_COLORS = ['#fff', '#666', '#000', '#abc', '#def', '#abcdef'];
class Interactive extends React.Component<{}, { value: string; colors: string[] }> {
public state = {
value: '',
colors: SIX_COLORS,
};
public render() {
return (
<ColorPicker
colors={this.state.colors}
onAddColor={value => this.setState({ colors: this.state.colors.concat(value) })}
onRemoveColor={value =>
this.setState({ colors: this.state.colors.filter(color => color !== value) })
}
onChange={value => this.setState({ value })}
value={this.state.value}
/>
);
}
}
storiesOf('components/ColorPicker', module)
.addParameters({
info: {
inline: true,
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '40px 60px',
width: '320px',
},
},
},
})
.add('three colors', () => (
<ColorPicker
value="#fff"
onAddColor={action('onAddColor')}
onRemoveColor={action('onRemoveColor')}
onChange={action('onChange')}
colors={THREE_COLORS}
/>
))
.add('six colors', () => (
<ColorPicker
value="#fff"
onAddColor={action('onAddColor')}
onRemoveColor={action('onRemoveColor')}
onChange={action('onChange')}
colors={SIX_COLORS}
/>
))
.add('six colors, value missing', () => (
<ColorPicker
value="#a1b2c3"
onAddColor={action('onAddColor')}
onRemoveColor={action('onRemoveColor')}
onChange={action('onChange')}
colors={SIX_COLORS}
/>
))
.add('interactive', () => <Interactive />, {
info: {
inline: true,
source: false,
propTablesExclude: [Interactive],
styles: {
infoBody: {
margin: 20,
},
infoStory: {
margin: '40px 60px',
width: '320px',
},
},
},
});

View file

@ -1,32 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { ColorPalette } from '../color_palette';
import { ColorManager } from '../color_manager';
export const ColorPicker = ({ onChange, value, colors, addColor, removeColor }) => {
return (
<div>
<ColorPalette onChange={onChange} value={value} colors={colors} />
<ColorManager
onChange={onChange}
value={value}
addColor={addColor}
removeColor={removeColor}
/>
</div>
);
};
ColorPicker.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func,
colors: PropTypes.array,
addColor: PropTypes.func,
removeColor: PropTypes.func,
};

View file

@ -0,0 +1,75 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import PropTypes from 'prop-types';
import React, { SFC } from 'react';
import tinycolor from 'tinycolor2';
import { ColorManager } from '../color_manager';
import { ColorPalette } from '../color_palette';
export interface Props {
/**
* An array of hexadecimal color values. Non-hex will be ignored.
* @default []
*/
colors?: string[];
/** The function to call when the Add Color button is clicked. The button will not appear if there is no handler. */
onAddColor?: (value: string) => void;
/** The function to call when the color is changed. */
onChange: (value: string) => void;
/** The function to call when the Remove Color button is clicked. The button will not appear if there is no handler. */
onRemoveColor?: (value: string) => void;
/**
* The value of the color in the selector. Should be hexadecimal. If it is not in the colors array, it will be ignored.
* @default ''
*/
value?: string;
}
export const ColorPicker: SFC<Props> = ({
colors = [],
value = '',
onAddColor,
onChange,
onRemoveColor,
}) => {
const tc = tinycolor(value);
const isValidColor = tc.isValid() && tc.getFormat() === 'hex';
colors = colors.filter(color => {
const providedColor = tinycolor(color);
return providedColor.isValid() && providedColor.getFormat() === 'hex';
});
let canRemove = false;
let canAdd = false;
if (isValidColor) {
const match = colors.filter(color => tinycolor.equals(value, color));
canRemove = match.length > 0;
canAdd = match.length === 0;
}
return (
<div>
<ColorPalette onChange={onChange} value={value} colors={colors} />
<ColorManager
onChange={onChange}
value={value}
onAddColor={canAdd ? onAddColor : undefined}
onRemoveColor={canRemove ? onRemoveColor : undefined}
/>
</div>
);
};
ColorPicker.propTypes = {
colors: PropTypes.array,
onAddColor: PropTypes.func,
onChange: PropTypes.func.isRequired,
onRemoveColor: PropTypes.func,
value: PropTypes.string,
};

View file

@ -0,0 +1,227 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Storyshots components/ItemGrid color dot grid 1`] = `
<div
className="item-grid-row"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(255, 255, 255)",
}
}
/>
</div>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(102, 102, 102)",
}
}
/>
</div>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(0, 0, 0)",
}
}
/>
</div>
</div>
`;
exports[`Storyshots components/ItemGrid complex grid 1`] = `
<div
className="item-grid-row"
>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(255, 255, 255)",
}
}
>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={
Object {
"fill": "#333",
}
}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
</div>
</div>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(102, 102, 102)",
}
}
>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={
Object {
"fill": "#FFF",
}
}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
</div>
</div>
<div
className="canvasColorDot"
>
<div
className="canvasColorDot__background canvasCheckered"
/>
<div
className="canvasColorDot__foreground"
style={
Object {
"background": "rgb(0, 0, 0)",
}
}
>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={
Object {
"fill": "#FFF",
}
}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.5 12a.502.502 0 0 1-.354-.146l-4-4a.502.502 0 0 1 .708-.708L6.5 10.793l6.646-6.647a.502.502 0 0 1 .708.708l-7 7A.502.502 0 0 1 6.5 12"
fillRule="evenodd"
/>
</svg>
</div>
</div>
</div>
`;
exports[`Storyshots components/ItemGrid icon grid 1`] = `
<div
className="item-grid-row"
>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 7h3.5a.5.5 0 1 1 0 1H8v3.5a.5.5 0 1 1-1 0V8H3.5a.5.5 0 0 1 0-1H7V3.5a.5.5 0 0 1 1 0V7zm-.5-7C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882z"
fillRule="evenodd"
/>
</svg>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 0C11.636 0 15 3.364 15 7.5S11.636 15 7.5 15 0 11.636 0 7.5 3.364 0 7.5 0zm0 .882a6.618 6.618 0 1 0 0 13.236A6.618 6.618 0 0 0 7.5.882zM3.5 7h8a.5.5 0 1 1 0 1h-8a.5.5 0 0 1 0-1z"
fillRule="evenodd"
/>
</svg>
<svg
className="euiIcon euiIcon--medium"
focusable="false"
height="16"
style={null}
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.5 12a.502.502 0 0 1-.354-.146l-4-4a.502.502 0 0 1 .708-.708L6.5 10.793l6.646-6.647a.502.502 0 0 1 .708.708l-7 7A.502.502 0 0 1 6.5 12"
fillRule="evenodd"
/>
</svg>
</div>
`;
exports[`Storyshots components/ItemGrid simple grid 1`] = `
<div
className="item-grid-row"
>
<div>
a
</div>
<div>
b
</div>
<div>
c
</div>
</div>
`;

View file

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiIcon, IconType } from '@elastic/eui';
import { storiesOf } from '@storybook/react';
import React from 'react';
import { readableColor } from '../../../lib/readable_color';
import { ColorDot } from '../../color_dot';
import { ItemGrid } from '../item_grid';
storiesOf('components/ItemGrid', module)
.add('simple grid', () => (
<ItemGrid items={['a', 'b', 'c']} children={item => <div key={item}>{item}</div>} />
))
.add('icon grid', () => (
<ItemGrid
items={['plusInCircle', 'minusInCircle', 'check'] as IconType[]}
children={(item: IconType) => <EuiIcon key={item} type={item} />}
/>
))
.add('color dot grid', () => (
<ItemGrid items={['#fff', '#666', '#000']}>
{item => <ColorDot key={item} value={item} />}
</ItemGrid>
))
.add('complex grid', () => (
<ItemGrid
items={
[
{ color: '#fff', icon: 'plusInCircle' },
{ color: '#666', icon: 'minusInCircle' },
{ color: '#000', icon: 'check' },
] as Array<{ color: string; icon: IconType }>
}
>
{item => (
<ColorDot key={item.color} value={item.color}>
<EuiIcon type={item.icon} color={readableColor(item.color)} />
</ColorDot>
)}
</ItemGrid>
));

View file

@ -4,8 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { compose, withState } from 'recompose';
import { pure } from 'recompose';
import { ItemGrid as Component, Props as ComponentProps } from './item_grid';
import { ColorManager as Component } from './color_manager';
export const ColorManager = compose(withState('adding', 'setAdding', false))(Component);
export const ItemGrid = pure<ComponentProps<any>>(Component);

View file

@ -1,42 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { last } from 'lodash';
const defaultPerRow = 6;
export const ItemGrid = ({ items, itemsPerRow, children }) => {
if (!items) {
return null;
}
const rows = items.reduce(
(rows, item) => {
if (last(rows).length >= (itemsPerRow || defaultPerRow)) {
rows.push([]);
}
last(rows).push(children({ item }));
return rows;
},
[[]]
);
return rows.map((row, i) => (
<div key={`item-grid-row-${i}`} className="item-grid-row">
{row}
</div>
));
};
ItemGrid.propTypes = {
items: PropTypes.array.isRequired,
itemsPerRow: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
children: PropTypes.func.isRequired,
};

View file

@ -0,0 +1,65 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { last } from 'lodash';
import PropTypes from 'prop-types';
import React, { Fragment, ReactElement, ValidationMap } from 'react';
const PER_ROW_DEFAULT = 6;
export interface Props<T> {
/** A collection of 'things' to be iterated upon by the children prop function. */
items: T[];
/**
* The number of items per row.
* @default 6
*/
itemsPerRow?: number;
/** A function with which to iterate upon the items collection, producing nodes. */
children: (item: T) => ReactElement<any>;
}
// We need this type in order to define propTypes on the object. It's a bit redundant,
// but TS needs to know that ItemGrid can have propTypes defined on it.
interface ItemGridType {
<T>(props: Props<T>): ReactElement<any>;
propTypes?: ValidationMap<Props<any>>;
}
export const ItemGrid: ItemGridType = function ItemGridFunc<T>({
items = [],
itemsPerRow = PER_ROW_DEFAULT,
children,
}: Props<T>) {
const reducedRows = items.reduce(
(rows: Array<Array<ReactElement<any>>>, item: any) => {
if (last(rows).length >= itemsPerRow) {
rows.push([]);
}
last(rows).push(children(item));
return rows;
},
[[]] as Array<Array<ReactElement<any>>>
);
return (
<Fragment>
{reducedRows.map((row, i) => (
<div key={`item-grid-row-${i}`} className="item-grid-row">
{row}
</div>
))}
</Fragment>
);
};
ItemGrid.propTypes = {
items: PropTypes.array,
itemsPerRow: PropTypes.number,
children: PropTypes.func.isRequired,
};

View file

@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { readableColor } from '../readable_color';
describe('readableColor', () => {
test('light', () => {
expect(readableColor('#000')).toEqual('#FFF');
expect(readableColor('#000', '#EEE', '#111')).toEqual('#EEE');
expect(readableColor('#111')).toEqual('#FFF');
expect(readableColor('#111', '#EEE', '#111')).toEqual('#EEE');
});
test('dark', () => {
expect(readableColor('#FFF')).toEqual('#333');
expect(readableColor('#FFF', '#EEE', '#111')).toEqual('#111');
expect(readableColor('#EEE')).toEqual('#333');
expect(readableColor('#EEE', '#EEE', '#111')).toEqual('#111');
});
});

View file

@ -6,9 +6,7 @@
import chroma from 'chroma-js';
export function readableColor(background, light, dark) {
light = light || '#FFF';
dark = dark || '#333';
export function readableColor(background: string, light: string = '#FFF', dark: string = '#333') {
try {
return chroma.contrast(background, '#000') < 7 ? light : dark;
} catch (e) {

View file

@ -1648,6 +1648,11 @@
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.10.tgz#780d552467824be4a241b29510a7873a7432c4a6"
integrity sha512-fOM/Jhv51iyugY7KOBZz2ThfT1gwvsGCfWxpLpZDgkGjpEO4Le9cld07OdskikLjDUQJ43dzDaVRSFwQlpdqVg==
"@types/chroma-js@^1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@types/chroma-js/-/chroma-js-1.4.1.tgz#7c52d461173d569ba1f27e0c2dd26ee76691ec82"
integrity sha512-i9hUiO3bwgmzZUDwBuR65WqsBQ/nwN+H2fKX0bykXCdd8cFQEuIj8vI7FXjyb2f5z5h+pv76I/uakikKSgaqTA==
"@types/chromedriver@^2.38.0":
version "2.38.0"
resolved "https://registry.yarnpkg.com/@types/chromedriver/-/chromedriver-2.38.0.tgz#971032b73eb7f44036f4f5bed59a7fd5b468014f"
@ -2236,6 +2241,13 @@
dependencies:
"@types/react" "*"
"@types/recompose@^0.30.5":
version "0.30.5"
resolved "https://registry.yarnpkg.com/@types/recompose/-/recompose-0.30.5.tgz#09890e3c504546b38193479e610e427ac0888393"
integrity sha512-PEQvFmudB9n0+ZvD8l7lh0olGAWmVAuVwCM4eotzWouH8/Kcr8/EcZyLhYILqoTlqzi6ey/3kbKQzJ/h3KkyXw==
dependencies:
"@types/react" "*"
"@types/reduce-reducers@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@types/reduce-reducers/-/reduce-reducers-0.1.3.tgz#69f252207622ced7e063c7526ad46ec60b69f0c0"
@ -2343,6 +2355,11 @@
resolved "https://registry.yarnpkg.com/@types/tempy/-/tempy-0.1.0.tgz#8ba0339dcd5abb554f301683dc3396d153ec5bfd"
integrity sha512-2qeSxI2bMucW58Jsj8jrBXZxobtcKkvO44AvJzKGaD8+m/3KRuBqeKitJ5U6sqy3a9tFsqhzsxMkqR4Wcl6AmQ==
"@types/tinycolor2@^1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.1.tgz#2f5670c9d1d6e558897a810ed284b44918fc1253"
integrity sha512-25L/RL5tqZkquKXVHM1fM2bd23qjfbcPpAZ2N/H05Y45g3UEi+Hw8CbDV28shKY8gH1SHiLpZSxPI1lacqdpGg==
"@types/type-detect@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/type-detect/-/type-detect-4.0.1.tgz#3b0f5ac82ea630090cbf57c57a1bf5a63a29b9b6"