Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #2804: Use chroma-js for all color parsing and conversion #8000

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/eui/changelogs/upcoming/8000.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- Updated color parsing and conversion methods to use chroma-js

- Methods changed
- hexToHsv
- hexToRgb
- hsvToHex
- hsvToRgb
- rgbToHex
- rgbToHsv

21 changes: 15 additions & 6 deletions packages/eui/src/services/color/hex_to_hsv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
22 changes: 9 additions & 13 deletions packages/eui/src/services/color/hex_to_rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 12 additions & 5 deletions packages/eui/src/services/color/hsv_to_hex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 '';
}
26 changes: 11 additions & 15 deletions packages/eui/src/services/color/hsv_to_rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
3 changes: 3 additions & 0 deletions packages/eui/src/services/color/is_valid_hex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
23 changes: 15 additions & 8 deletions packages/eui/src/services/color/rgb_to_hex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,28 @@
* 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
);
if (!rgbMatch) {
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 '';
}
44 changes: 15 additions & 29 deletions packages/eui/src/services/color/rgb_to_hsv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}