diff --git a/src-docs/src/components/guide_components.scss b/src-docs/src/components/guide_components.scss
index 87aa190e3969..885858863c86 100644
--- a/src-docs/src/components/guide_components.scss
+++ b/src-docs/src/components/guide_components.scss
@@ -150,6 +150,21 @@ $guideZLevelHighest: $euiZLevel9 + 1000;
outline: solid 2px purple;
}
+.guideColorPalette__swatch {
+
+ span {
+ height: $euiSize;
+ width: $euiSizeL;
+ }
+
+ &:first-child span {
+ border-radius: $euiBorderRadius 0 0 $euiBorderRadius;
+ }
+
+ &:last-child span {
+ border-radius: 0 $euiBorderRadius $euiBorderRadius 0;
+ }
+}
@import "../views/guidelines/index";
@import "guide_section/index";
@@ -193,4 +208,21 @@ $guideZLevelHighest: $euiZLevel9 + 1000;
.guidePageContent {
margin-left: 0;
}
+
+ .euiFlexGroup--responsive > .euiFlexItem.guideColorPalette__swatch {
+ margin-bottom: 0 !important;
+
+ span {
+ height: $euiSize;
+ width: $euiSizeL;
+ }
+
+ &:first-child span {
+ border-radius: $euiBorderRadius $euiBorderRadius 0 0;
+ }
+
+ &:last-child span {
+ border-radius: 0 0 $euiBorderRadius $euiBorderRadius;
+ }
+ }
}
diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js
index 032c8d639f12..cc725d307c61 100644
--- a/src-docs/src/routes.js
+++ b/src-docs/src/routes.js
@@ -36,6 +36,9 @@ import WritingGuidelines
// Services
+import { ColorPaletteExample }
+ from './views/color_palette/color_palette_example';
+
import { IsColorDarkExample }
from './views/is_color_dark/is_color_dark_example';
@@ -395,17 +398,18 @@ const navigation = [{
name: 'Utilities',
items: [
AccessibilityExample,
+ ColorPaletteExample,
CopyExample,
- ResponsiveExample,
+ UtilityClassesExample,
DelayHideExample,
ErrorBoundaryExample,
HighlightExample,
IsColorDarkExample,
+ MutationObserverExample,
OutsideClickDetectorExample,
PortalExample,
+ ResponsiveExample,
ToggleExample,
- UtilityClassesExample,
- MutationObserverExample,
WindowEventExample,
].map(example => createExample(example)),
}, {
diff --git a/src-docs/src/views/color_palette/color_palette.js b/src-docs/src/views/color_palette/color_palette.js
new file mode 100644
index 000000000000..de6251c7d7b9
--- /dev/null
+++ b/src-docs/src/views/color_palette/color_palette.js
@@ -0,0 +1,38 @@
+import React, { Fragment } from 'react';
+
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiTitle,
+ EuiSpacer,
+} from '../../../../src/components';
+
+import {
+ colorPalette,
+ palettes,
+} from '../../../../src/services';
+
+const availablePalettes = Object.keys(palettes);
+
+export default () => (
+
+ {
+ availablePalettes.map((paletteName, i) => (
+
+ {paletteName}
+
+
+ {
+ colorPalette(paletteName).map((hexCode, j) => (
+
+
+
+ ))
+ }
+
+
+
+ ))
+ }
+
+);
diff --git a/src-docs/src/views/color_palette/color_palette_custom.js b/src-docs/src/views/color_palette/color_palette_custom.js
new file mode 100644
index 000000000000..63b21d7a51ff
--- /dev/null
+++ b/src-docs/src/views/color_palette/color_palette_custom.js
@@ -0,0 +1,52 @@
+import React, { Fragment } from 'react';
+
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiTitle,
+ EuiSpacer,
+} from '../../../../src/components';
+
+import {
+ colorPalette,
+} from '../../../../src/services';
+
+export default () => (
+
+ Custom red to blue
+
+
+ {
+ colorPalette('custom', 'FF0000', '#00FFFF', 25).map((hexCode, j) => (
+
+
+
+ ))
+ }
+
+
+ Custom yellow to green
+
+
+ {
+ colorPalette('custom', '#F7EE55', '#4EB265', 20).map((hexCode, k) => (
+
+
+
+ ))
+ }
+
+
+ Custom green to red
+
+
+ {
+ colorPalette('custom', '#4EB265', '#920000', 15).map((hexCode, l) => (
+
+
+
+ ))
+ }
+
+
+);
diff --git a/src-docs/src/views/color_palette/color_palette_example.js b/src-docs/src/views/color_palette/color_palette_example.js
new file mode 100644
index 000000000000..727ee96dce94
--- /dev/null
+++ b/src-docs/src/views/color_palette/color_palette_example.js
@@ -0,0 +1,79 @@
+import React from 'react';
+
+import { renderToHtml } from '../../services';
+
+import {
+ GuideSectionTypes,
+} from '../../components';
+
+import {
+ EuiCode,
+} from '../../../../src/components';
+
+import ColorPalette from './color_palette';
+const colorPaletteSource = require('!!raw-loader!./color_palette');
+const colorPaletteHtml = renderToHtml(ColorPalette);
+
+import ColorPaletteCustom from './color_palette_custom';
+const colorPaletteCustomSource = require('!!raw-loader!./color_palette_custom');
+const colorPaletteCustomHtml = renderToHtml(ColorPaletteCustom);
+
+import ColorPaletteHistogram from './color_palette_histogram';
+const colorPaletteHistogramSource = require('!!raw-loader!./color_palette_histogram');
+const colorPaletteHistogramHtml = renderToHtml(ColorPaletteHistogram);
+
+export const ColorPaletteExample = {
+ title: 'Color Palettes',
+ sections: [{
+ title: 'Preset color palettes',
+ source: [{
+ type: GuideSectionTypes.JS,
+ code: colorPaletteSource,
+ }, {
+ type: GuideSectionTypes.HTML,
+ code: colorPaletteHtml,
+ }],
+ text: (
+
+ Use the colorPalette service to obtain an array of
+ hexidecimal color codes for a given palette such as
+ colorPalette('color_blind'), then apply them to UI
+ elements such as charts.
+
+ ),
+ demo: ,
+ }, {
+ title: 'Custom color palettes',
+ source: [{
+ type: GuideSectionTypes.JS,
+ code: colorPaletteCustomSource,
+ }, {
+ type: GuideSectionTypes.HTML,
+ code: colorPaletteCustomHtml,
+ }],
+ text: (
+
+ Generate a custom palette of any length from two hexidecimal color
+ codes such as
+ colorPalette('custom', 'FF0000', '#00FFFF', 25).
+
+ ),
+ demo: ,
+ }, {
+ title: 'Chart example',
+ source: [{
+ type: GuideSectionTypes.JS,
+ code: colorPaletteHistogramSource,
+ }, {
+ type: GuideSectionTypes.HTML,
+ code: colorPaletteHistogramHtml,
+ }],
+ text: (
+
+ Apply the results of colorPalette to the
+ color prop of EUI chart components.
+
+ ),
+ demo: ,
+ }],
+};
diff --git a/src-docs/src/views/color_palette/color_palette_histogram.js b/src-docs/src/views/color_palette/color_palette_histogram.js
new file mode 100644
index 000000000000..4209ac2fcd0b
--- /dev/null
+++ b/src-docs/src/views/color_palette/color_palette_histogram.js
@@ -0,0 +1,56 @@
+import React, { Component, Fragment } from 'react';
+
+import {
+ EuiSeriesChart,
+ EuiHistogramSeries,
+ EuiSeriesChartUtils,
+} from '../../../../src/experimental';
+import {
+ colorPalette,
+} from '../../../../src/services/color/color_palette';
+
+const { SCALE } = EuiSeriesChartUtils;
+const timestamp = Date.now();
+const ONE_HOUR = 3600000;
+const margins = {
+ top: 10,
+ left: 80,
+ right: 0,
+ bottom: 20,
+};
+const colors = colorPalette('custom', 'FF0000', '#00FFFF', 6);
+
+function randomizeData(size = 24, max = 8) {
+ return new Array(size)
+ .fill(0)
+ .map((d, i) => ({
+ x0: ONE_HOUR * i,
+ x: ONE_HOUR * (i + 1),
+ y: Math.floor(Math.random() * max),
+ }))
+ .map(el => ({
+ x0: el.x0 + timestamp,
+ x: el.x + timestamp,
+ y: el.y,
+ }));
+}
+function buildData(series) {
+ const max = Math.ceil(Math.random() * 1000000);
+ return new Array(series).fill(0).map(() => randomizeData(20, max));
+}
+export default class Example extends Component {
+ state = {
+ series: 6,
+ data: buildData(6),
+ };
+ render() {
+ const { data } = this.state;
+ return (
+
+
+ {data.map((d, i) => )}
+
+
+ );
+ }
+}
diff --git a/src/services/color/color_palette.js b/src/services/color/color_palette.js
new file mode 100644
index 000000000000..83690faf597a
--- /dev/null
+++ b/src/services/color/color_palette.js
@@ -0,0 +1,163 @@
+import { palettes } from './eui_palettes';
+
+/**
+ * This function takes a color palette name and returns an array of hex color
+ * codes for use in UI elements such as charts.
+ *
+ * @param {string} paletteName The name of the palette being requested
+ * @param {string} hexStart The beginning hexidecimal color code
+ * @param {string} hexEnd The beginning hexidecimal color code
+ * @param {number} len The number of colors in the resulting array
+ * @returns {Array}
+ */
+
+function colorPalette(paletteName, hexStart, hexEnd, len = 0) {
+ if (typeof paletteName !== 'undefined' && paletteName !== 'custom') {
+ try {
+ const palette = palettes[paletteName];
+ const hexColors = palette.colors;
+ return hexColors;
+ } catch(e) {
+ const availablePalettes = Object.keys(palettes);
+ throw new Error(`${paletteName} is not a valid palette name. Please select from ${availablePalettes}`);
+ }
+ } else if (paletteName === 'custom') {
+ if (isHex(hexStart) && isHex(hexEnd)) {
+ const hex1 = formatHex(hexStart);
+ const hex2 = formatHex(hexEnd);
+ const customColors = generatePalette(hex1, hex2, len);
+ return customColors;
+ } else {
+ throw new Error('Please provide two valid hex color codes.');
+ }
+ }
+}
+
+/**
+ * Check if argument is a valid hexidecimal color code
+ */
+function isHex(value) {
+ return /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value);
+}
+
+/**
+ * Check if value can be interpreted as a number
+ */
+function isValid(c) {
+ let valid = 'n';
+ if ((!isNaN(c[0])) && (!isNaN(c[1])) && (!isNaN(c[2]))) {valid = 'y';}
+ return valid;
+}
+
+/**
+ * Create the hexideciaml color code string
+ */
+function createHex(c) {
+ let result = '';
+ let k = 0;
+ let val = 0;
+ let piece;
+ const d = 1;
+ const base = 16;
+ for (k = 0; k < 3; k++) {
+ val = Math.round(c[k] / d);
+ piece = val.toString(base);
+ if (piece.length < 2) {piece = `0${piece}`;}
+ result = result + piece;
+ }
+ result = `#${result.toUpperCase()}`;
+ return result;
+}
+
+/**
+ * Create the color object for manipulation by other functions
+ */
+class Color {
+ constructor(r, g, b) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.collection = new Array(r, g, b);
+ this.valid = isValid(this.collection);
+ this.text = createHex(this.collection);
+ }
+}
+
+/**
+ * Parse hex color into integer values
+ */
+function colorParse(color) {
+ const m = 1;
+ const base = 16;
+ let a;
+ let b;
+ let c = color.toUpperCase();
+ let col = c.replace(/[\#\(]*/i, '');
+
+ if (col.length === 3) {
+ a = col.substr(0, 1);
+ b = col.substr(1, 1);
+ c = col.substr(2, 1);
+ col = a + a + b + b + c + c;
+ }
+ const num = new Array(col.substr(0, 2), col.substr(2, 2), col.substr(4, 2));
+ const ret = new Array(parseInt(num[0], base) * m, parseInt(num[1], base) * m, parseInt(num[2], base) * m);
+ return(ret);
+}
+
+/**
+ * Format hex values to #RRGGBB
+ */
+function formatHex(hex) {
+ let cleanHex = hex;
+ if (cleanHex.length === 3 || cleanHex.length === 6) {
+ cleanHex = `#${cleanHex}`;
+ }
+ if (cleanHex.length === 4) {
+ cleanHex = cleanHex.split('');
+ cleanHex = cleanHex[0] + cleanHex[1] + cleanHex[1] + cleanHex[2] + cleanHex[2] + cleanHex[3] + cleanHex[3];
+ }
+ return cleanHex;
+}
+
+/**
+ * Calculate the step increment for each piece of the hexidecimal color code
+ */
+function stepCalc(st, cStart, cEnd) {
+ const steps = st;
+ const step = new Array(3);
+ step[0] = (cEnd.r - cStart.r) / steps;
+ step[1] = (cEnd.g - cStart.g) / steps;
+ step[2] = (cEnd.b - cStart.b) / steps;
+
+ return step;
+}
+
+/**
+ * Generate a custom plette from two hexidecimal color code values
+ */
+function generatePalette(start, end, len) {
+ const colorArray = new Array();
+ const hexPalette = new Array();
+ const count = len - 1;
+ const startHex = colorParse(start);
+ const endHex = colorParse(end);
+ let i = 1;
+ let step = new Array(3);
+ colorArray[0] = new Color(startHex[0], startHex[1], startHex[2]); // first color
+ colorArray[count] = new Color(endHex[0], endHex[1], endHex[2]); // last color
+ step = stepCalc(count, colorArray[0], colorArray[count]);
+ hexPalette[0] = colorArray[0].text;
+ for (i = 1; i < count; i++) {
+ const r = (colorArray[0].r + (step[0] * i));
+ const g = (colorArray[0].g + (step[1] * i));
+ const b = (colorArray[0].b + (step[2] * i));
+ colorArray[i] = new Color(r, g, b);
+ hexPalette[i] = colorArray[i].text;
+ } // all the colors in between
+ hexPalette[count] = colorArray[count].text;
+
+ return hexPalette;
+}
+
+export { colorPalette };
diff --git a/src/services/color/eui_palettes.js b/src/services/color/eui_palettes.js
new file mode 100644
index 000000000000..bdc70ffc37e1
--- /dev/null
+++ b/src/services/color/eui_palettes.js
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+export const palettes = {
+ color_blind: {
+ colors: [
+ '#1ea593',
+ '#2b70f7',
+ '#ce0060',
+ '#38007e',
+ '#fca5d3',
+ '#f37020',
+ '#e49e29',
+ '#b0916f',
+ '#7b000b',
+ '#34130c',
+ ],
+ },
+ color_blind_15: {
+ colors: [
+ '#000000',
+ '#004949',
+ '#009292',
+ '#ff6db6',
+ '#ffb6db',
+ '#490092',
+ '#006ddb',
+ '#b66dff',
+ '#6db6ff',
+ '#b6dbff',
+ '#920000',
+ '#924900',
+ '#db6d00',
+ '#24ff24',
+ '#ffff6d',
+ ]
+ },
+ eui_colors: {
+ colors: [
+ '#0079A5',
+ '#017F75',
+ '#E5830E',
+ '#A30000',
+ '#DD0A73',
+ ]
+ },
+ eui_colors_light: {
+ colors: [
+ '#009ED8',
+ '#01B2A4',
+ '#F39B33',
+ '#D60000',
+ '#F5258C',
+ ]
+ },
+ spectrum: {
+ colors: [
+ '#882E72',
+ '#B178A6',
+ '#D6C1DE',
+ '#1965B0',
+ '#5289C7',
+ '#7BAFDE',
+ '#4EB265',
+ '#90C987',
+ '#CAE0AB',
+ '#F7EE55',
+ '#F6C141',
+ '#F1932D',
+ '#E8601C',
+ '#DC050C',
+ ]
+ }
+};
diff --git a/src/services/color/index.js b/src/services/color/index.js
index 0c4908fe99b4..c31c6b8da199 100644
--- a/src/services/color/index.js
+++ b/src/services/color/index.js
@@ -3,3 +3,5 @@ export { hexToRgb } from './hex_to_rgb';
export { rgbToHex } from './rgb_to_hex';
export { calculateContrast, calculateLuminance } from './luminance_and_contrast';
export { VISUALIZATION_COLORS, DEFAULT_VISUALIZATION_COLOR } from './visualization_colors';
+export { colorPalette } from './color_palette';
+export { palettes } from './eui_palettes';
diff --git a/src/services/index.js b/src/services/index.js
index de7a563cc2a1..aabefafa4cdb 100644
--- a/src/services/index.js
+++ b/src/services/index.js
@@ -23,6 +23,8 @@ export {
rgbToHex,
VISUALIZATION_COLORS,
DEFAULT_VISUALIZATION_COLOR,
+ colorPalette,
+ palettes,
} from './color';
export {