diff --git a/packages/eui/changelogs/upcoming/8000.md b/packages/eui/changelogs/upcoming/8000.md new file mode 100644 index 00000000000..399d3414ccf --- /dev/null +++ b/packages/eui/changelogs/upcoming/8000.md @@ -0,0 +1,10 @@ +- Updated color parsing and conversion methods to use chroma-js + +- Methods changed + - hexToHsv + - hexToRgb + - hsvToHex + - hsvToRgb + - rgbToHex + - rgbToHsv + diff --git a/packages/eui/src/services/color/hex_to_hsv.ts b/packages/eui/src/services/color/hex_to_hsv.ts index 2b6dcd09e04..47500804416 100644 --- a/packages/eui/src/services/color/hex_to_hsv.ts +++ b/packages/eui/src/services/color/hex_to_hsv.ts @@ -6,11 +6,20 @@ * Side Public License, v 1. */ -import { hexToRgb } from './hex_to_rgb'; -import { rgbToHsv } from './rgb_to_hsv'; -import { HEX, HSV } from './color_types'; +import type { HEX, HSV } from './color_types'; +import { hex, valid } from 'chroma-js'; -export function hexToHsv(hex: HEX): HSV { - const [r, g, b] = hexToRgb(hex); - return rgbToHsv({ r, g, b }); +export function hexToHsv(hexCode: HEX): HSV { + //Create a new chroma-js color from hexCode provided + const color = hex(hexCode); + + //If color valid convert from HEX to HSV + if (valid(color)) { + const [h, s, v] = color.hsv(); + + return { h, s, v }; + } + + // fallback to prevent errors + return { h: Number.NaN, s: Number.NaN, v: Number.NaN }; } diff --git a/packages/eui/src/services/color/hex_to_rgb.ts b/packages/eui/src/services/color/hex_to_rgb.ts index 036a745cf73..045710f5236 100644 --- a/packages/eui/src/services/color/hex_to_rgb.ts +++ b/packages/eui/src/services/color/hex_to_rgb.ts @@ -9,21 +9,17 @@ // Convert hexadecimal color into an array of RGB integer values // Modified from https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb -import { rgbDef } from './color_types'; +import type { rgbDef } from './color_types'; +import { hex, valid } from 'chroma-js'; -export function hexToRgb(hex: string): rgbDef { - // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") - const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace( - shorthandRegex, - (m, r1, g1, b1) => r1 + r1 + g1 + g1 + b1 + b1 - ); +export function hexToRgb(hexCode: string): rgbDef { + //Create a new chroma-js color from hexCode provided + const color = hex(hexCode); - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!; - - if (result) { - const [, r, g, b] = result; - return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)]; + //If color valid convert from HEX to RGB + if (valid(color)) { + const rgb = color.rgb(); + return rgb; } // fallback to prevent errors diff --git a/packages/eui/src/services/color/hsv_to_hex.ts b/packages/eui/src/services/color/hsv_to_hex.ts index 050c53d947d..e1fe2c63065 100644 --- a/packages/eui/src/services/color/hsv_to_hex.ts +++ b/packages/eui/src/services/color/hsv_to_hex.ts @@ -6,11 +6,18 @@ * Side Public License, v 1. */ -import { hsvToRgb } from './hsv_to_rgb'; -import { rgbToHex } from './rgb_to_hex'; -import { HEX, HSV } from './color_types'; +import type { HEX, HSV } from './color_types'; +import { hsv, valid } from 'chroma-js'; export function hsvToHex({ h, s, v }: HSV): HEX { - const { r, g, b } = hsvToRgb({ h, s, v }); - return rgbToHex(`rgb(${r}, ${g}, ${b})`); + //Create a new chroma-js color from HSV provided + const color = hsv(h, s, v); + + //If color valid convert HSV to HEX + if (valid(color)) { + return color.hex(); + } + + // fallback to prevent errors + return ''; } diff --git a/packages/eui/src/services/color/hsv_to_rgb.ts b/packages/eui/src/services/color/hsv_to_rgb.ts index 26d1e13df26..377f5a0c810 100644 --- a/packages/eui/src/services/color/hsv_to_rgb.ts +++ b/packages/eui/src/services/color/hsv_to_rgb.ts @@ -6,23 +6,19 @@ * Side Public License, v 1. */ -import { HSV, RGB } from './color_types'; +import type { HSV, RGB } from './color_types'; +import { hsv, valid } from 'chroma-js'; export function hsvToRgb({ h, s, v }: HSV): RGB { - h /= 60; + //Create a new chroma-js color from HSV provided + const color = hsv(h, s, v); - const fn = (n: number) => { - const k = (n + h) % 6; - return v - v * s * Math.max(Math.min(k, 4 - k, 1), 0); - }; + //If color valid convert HSV to RGB + if (valid(color)) { + const [r, g, b] = color.rgb(); + return { r, g, b }; + } - const r = fn(5); - const g = fn(3); - const b = fn(1); - - return { - r: Math.round(r * 255), - g: Math.round(g * 255), - b: Math.round(b * 255), - }; + // fallback to prevent errors + return { r: 0, g: 0, b: 0 }; } diff --git a/packages/eui/src/services/color/is_valid_hex.ts b/packages/eui/src/services/color/is_valid_hex.ts index c253cb72a0e..6a833bc7f13 100644 --- a/packages/eui/src/services/color/is_valid_hex.ts +++ b/packages/eui/src/services/color/is_valid_hex.ts @@ -6,6 +6,9 @@ * Side Public License, v 1. */ +// Using chroma.js valid function wasn't appropriate as hex strings without # at the start are considered valids +// A verification for # at the start could be a solution, but I let you guys decide this + export function isValidHex(hex: string): boolean { return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex); } diff --git a/packages/eui/src/services/color/rgb_to_hex.ts b/packages/eui/src/services/color/rgb_to_hex.ts index 6629697943f..f18ec741f96 100644 --- a/packages/eui/src/services/color/rgb_to_hex.ts +++ b/packages/eui/src/services/color/rgb_to_hex.ts @@ -6,13 +6,10 @@ * Side Public License, v 1. */ -function asHex(value: string): string { - const hex = parseInt(value, 10).toString(16); - return hex.length === 1 ? `0${hex}` : hex; -} +import { rgb, valid } from 'chroma-js'; -export function rgbToHex(rgb: string): string { - const withoutWhitespace = rgb.replace(/\s+/g, ''); +export function rgbToHex(rgbString: string): string { + const withoutWhitespace = rgbString.replace(/\s+/g, ''); const rgbMatch = withoutWhitespace.match( /^rgba?\((\d+),(\d+),(\d+)(?:,(?:1(?:\.0*)?|0(?:\.\d+)?))?\)$/i ); @@ -20,7 +17,17 @@ export function rgbToHex(rgb: string): string { return ''; } - const [, r, g, b] = rgbMatch; + //Automatically converts rgb constants to number as chroma don't take strings + const [, r, g, b] = rgbMatch.map(Number); + + //Create a new chroma-js color from RGB provided + const color = rgb(r, g, b); + + //If color valid convert RGB to HEX + if (valid(color)) { + return color.hex(); + } - return `#${asHex(r)}${asHex(g)}${asHex(b)}`; + // fallback to prevent errors + return ''; } diff --git a/packages/eui/src/services/color/rgb_to_hsv.ts b/packages/eui/src/services/color/rgb_to_hsv.ts index 78e7d6ba8bc..25ebdad05cc 100644 --- a/packages/eui/src/services/color/rgb_to_hsv.ts +++ b/packages/eui/src/services/color/rgb_to_hsv.ts @@ -6,36 +6,22 @@ * Side Public License, v 1. */ -import { HSV, RGB } from './color_types'; +import type { HSV, RGB } from './color_types'; +import { rgb, valid } from 'chroma-js'; export function rgbToHsv({ r, g, b }: RGB): HSV { - r /= 255; - g /= 255; - b /= 255; - const max = Math.max(r, g, b); - const min = Math.min(r, g, b); - const delta = max - min; - let hue; - const value = max; - const saturation = max === 0 ? 0 : delta / max; - switch (max) { - case min: - default: - hue = 0; - break; - case r: - hue = (g - b) / delta + (g < b ? 6 : 0); - break; - case g: - hue = (b - r) / delta + 2; - break; - case b: - hue = (r - g) / delta + 4; - break; + //Create a new chroma-js color from RGB provided + const color = rgb(r, g, b); + + //If color valid convert RGB to HSV + if (valid(color)) { + let [h, s, v] = color.hsv(); + + h = Number.isNaN(h) ? 0 : h; + + return { h, s, v }; } - return { - h: hue * 60, - s: saturation, - v: value, - }; + + // fallback to prevent errors + return { h: Number.NaN, s: Number.NaN, v: Number.NaN }; }