Skip to content

Commit

Permalink
Support RGB percentage values
Browse files Browse the repository at this point in the history
Fixes #13
  • Loading branch information
sindresorhus committed Sep 1, 2023
1 parent 214528a commit 66bb951
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 10 deletions.
48 changes: 42 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
// TODO: Remove this ignore comment.
// eslint-disable-next-line no-mixed-operators
const toHex = (red, green, blue, alpha) => ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha;

export default function rgbHex(red, green, blue, alpha) {
const isPercent = (red + (alpha || '')).toString().includes('%');
let isPercent = (red + (alpha || '')).toString().includes('%');

if (typeof red === 'string' && !green) { // Single string parameter.
const parsed = parseCssRgbString(red);
if (!parsed) {
throw new TypeError('Invalid or unsupported color format.');
}

if (typeof red === 'string') {
[red, green, blue, alpha] = red.match(/(0?\.?\d+)%?\b/g).map(component => Number(component));
isPercent = false;
[red, green, blue, alpha] = parsed;
} else if (alpha !== undefined) {
alpha = Number.parseFloat(alpha);
}
Expand Down Expand Up @@ -31,7 +41,33 @@ export default function rgbHex(red, green, blue, alpha) {
alpha = '';
}

// TODO: Remove this ignore comment.
// eslint-disable-next-line no-mixed-operators
return ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha;
return toHex(red, green, blue, alpha);
}

const parseCssRgbString = input => {
const parts = input.replace(/rgba?\(([^)]+)\)/, '$1').split(/[,\s/]+/).filter(Boolean);
if (parts.length < 3) {
return;
}

const parseValue = (value, max) => {
value = value.trim();

if (value.endsWith('%')) {
return Math.min(Number.parseFloat(value) * max / 100, max);
}

return Math.min(Number.parseFloat(value), max);
};

const red = parseValue(parts[0], 255);
const green = parseValue(parts[1], 255);
const blue = parseValue(parts[2], 255);
let alpha;

if (parts.length === 4) {
alpha = parseValue(parts[3], 1);
}

return [red, green, blue, alpha];
};
22 changes: 18 additions & 4 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,30 @@ import rgbHex from './index.js';
test('main', t => {
t.is(rgbHex(0, 0, 0), '000000');
t.is(rgbHex(65, 131, 196), '4183c4');
t.is(rgbHex('255 154 253'), 'ff9afd');
t.is(rgbHex('rgb(40, 42, 54)'), '282a36');
t.is(rgbHex(0, 0, 0, 0), '00000000');
t.is(rgbHex(0, 0, 0, 0.5), '00000080');
t.is(rgbHex(0, 0, 0, '50%'), '00000080');
t.is(rgbHex(0, 0, 0, '100%'), '000000ff');
t.is(rgbHex(65, 131, 196, 0.2), '4183c433');
t.is(rgbHex('255 154 253, 0.8'), 'ff9afdcc');
t.is(rgbHex('160 82 45 .4'), 'a0522d66');
t.is(rgbHex(40, 42, 54, 0.75), '282a36bf');
t.is(rgbHex(40, 42, 54, '75%'), '282a36bf');
});

test('string - value', t => {
t.is(rgbHex('rgba(40, 42, 54, 75%)'), '282a36bf');
t.is(rgbHex('rgba( 40, 42, 54, 75% )'), '282a36bf');
t.is(rgbHex('40, 42, 54, 75%'), '282a36bf');
t.is(rgbHex('40, 42, 54'), '282a36');
t.is(rgbHex('255 154 253'), 'ff9afd');
t.is(rgbHex('255 154 253, 0.8'), 'ff9afdcc');
t.is(rgbHex('160 82 45 .4'), 'a0522d66');
});

test('string - percentage', t => {
t.is(rgbHex('rgb(30%, 20%, 50%)'), '4c337f');
t.is(rgbHex('100%, 50%, 0%'), 'ff7f00');
t.is(rgbHex('30%, 20%, 50%'), '4c337f');
t.is(rgbHex('30%, 20%, 50%, 50%'), '4c337f80');
t.is(rgbHex('30% 20% 50%'), '4c337f');
t.is(rgbHex('30% 20% 50% / 50%'), '4c337f80');
});

0 comments on commit 66bb951

Please sign in to comment.