这是indexloc提供的服务,不要输入任何密码
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
declare function hexToRgba(color: string, alpha?: string | number): string;
declare function hexToRgba(color: string, alpha?: string | number, parseRgb?: boolean): string;

export = hexToRgba;
18 changes: 14 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isRgb, rgbToRgba } from './rgb-parser';

const removeHash = hex => (hex.charAt(0) === '#' ? hex.slice(1) : hex);

const parseHex = (nakedHex) => {
Expand Down Expand Up @@ -48,12 +50,20 @@ const formatRgb = (decimalObject, parameterA) => {
*
* If you specify an alpha value, you'll get a rgba() value instead.
*
* @param The hex value to convert. ('123456'. '#123456', ''123', '#123')
* @param An alpha value to apply. (optional) ('0.5', '0.25')
* @param colorStr: The value to convert. ('123456', '#123456', ''123', '#123', rgb(0, 0, 0),
* rgba(0, 1, 2, 1) )
* @param a: An alpha value to apply. (optional) ('0.5', '0.25')
* @param parseRgb: enable rgb and rgba string parsing. (optional) (true, false), false by default.
* Useful in situations where the input value is unpredictable (hex or rgb), but you still need to
* return an rgba string consistently.
* @return An rgb or rgba value. ('rgb(11, 22, 33)'. 'rgba(11, 22, 33, 0.5)')
*/
const hexToRgba = (hex, a) => {
const hashlessHex = removeHash(hex);
const hexToRgba = (colorStr, a, parseRgb = false) => {
if (parseRgb && isRgb(colorStr)) {
return rgbToRgba(colorStr, a);
}

const hashlessHex = removeHash(colorStr);
const hexObject = parseHex(hashlessHex);
const decimalObject = hexesToDecimals(hexObject);

Expand Down
34 changes: 34 additions & 0 deletions src/rgb-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class RgbParseError extends Error {
constructor(message) {
super(message);
this.name = 'RgbParseError';
}
}

// The long expressions are just to catch errors
const RE_RGB = /^rgb\( *(?:(?:[01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]) *, *){2}(?:[01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]) *\)$/;
const RE_RGBA = /^rgba\( *(?:(?:[01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]) *, *){3}(?:(?:0\.)?\d+) *\)$/;
const RE_ALPHA = /(?:0\.)?\d+ *(?=\))/;
const RE_NO_ALPHA = /\)/;
const RE_IS_RGB = /^rgba?/;

const isRgb = str => RE_IS_RGB.test(str);

const rgbToRgba = (str, a) => {
if (RE_RGB.test(str)) {
if (a !== undefined) {
return str.replace(RE_NO_ALPHA, `, ${a})`).replace(/rgb\(/, 'rgba(');
}
return str;
}

if (RE_RGBA.test(str)) {
return a !== undefined ? str.replace(RE_ALPHA, `${a}`) : str; // replace alpha if defined, otherwise don't
}

throw new RgbParseError(
`rgba? string is invalid, must be in the form rgba?('0-255', '0-255', '0-255', '0-1'?), not: ${str}`,
);
};

module.exports = { isRgb, rgbToRgba, RgbParseError };
181 changes: 181 additions & 0 deletions test/hex.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/* global describe it */
import assert from 'assert';
import hexToRgba from '..';

[false, true].forEach((parseRgb) => {
describe('hex-to-rgba', () => {
describe(`when parseRgb is ${parseRgb}`, () => {
describe('6-digit hex values, no a', () => {
it('should calculate correct rgb values', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('112233', undefined, parseRgb));
});

it('should ignore a leading hash sign', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('#112233', undefined, parseRgb));
});

it('should correctly calculate uppercase hex', () => {
assert.equal('rgba(127, 127, 127, 1)', hexToRgba('#7F7F7F', undefined, parseRgb));
});

it('should correctly calculate lowercase hex', () => {
assert.equal('rgba(127, 127, 127, 1)', hexToRgba('#7f7f7f', undefined, parseRgb));
});
});

describe('6-digit hex values, a as parameter', () => {
it('should calculate rgba values from hex and string alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('112233', '0.5', parseRgb));
});

it('should calculate rgba values from hex and numerical alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.75)', hexToRgba('112233', 0.75, parseRgb));
});

it('should handle the edge case where alpha value is 1', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('112233', 1, parseRgb));
});

it('should handle the edge case where alpha value is 0', () => {
assert.equal('rgba(17, 34, 51, 0)', hexToRgba('112233', 0, parseRgb));
});

it('should calculate rgba values from hex with leading hash and string alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('#112233', '0.5', parseRgb));
});

it('should calculate rgba values from hex with leading hash and numerical alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.75)', hexToRgba('#112233', 0.75, parseRgb));
});
});

describe('3-digit hex values, no a', () => {
it('should calculate correct rgb values', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('123', undefined, parseRgb));
});

it('should ignore a leading hash sign', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('#123', undefined, parseRgb));
});
});

describe('3-digit hex values, a as parameter', () => {
it('should calculate rgba values from hex and string alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('123', '0.5', parseRgb));
});

it('should calculate rgba values from hex and numerical alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.75)', hexToRgba('123', 0.75, parseRgb));
});

it('should handle the edge case where alpha value is 1', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('123', 1, parseRgb));
});

it('should handle the edge case where alpha value is 0', () => {
assert.equal('rgba(17, 34, 51, 0)', hexToRgba('123', 0, parseRgb));
});

it('should calculate rgba values from hex with leading hash and string alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('#123', '0.5', parseRgb));
});

it('should calculate rgba values from hex with leading hash and numerical alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.75)', hexToRgba('#123', 0.75, parseRgb));
});
});

describe('8-digit hex values, no a', () => {
it('should calculate correct rgb values', () => {
assert.equal('rgba(17, 34, 51, 0.27)', hexToRgba('11223344', undefined, parseRgb));
});

it('should ignore a leading hash sign', () => {
assert.equal('rgba(17, 34, 51, 0.27)', hexToRgba('#11223344', undefined, parseRgb));
});

it('should remove trailing zeros', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('1122337f', undefined, parseRgb));
});

it('should handle the edge case where alpha value is 1', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('112233ff', undefined, parseRgb));
});

it('should handle the edge case where alpha value is 0', () => {
assert.equal('rgba(17, 34, 51, 0)', hexToRgba('112233', 0, parseRgb));
});
});

describe('8-digit hex values, a as parameter (separate parameter should override alpha in hex)', () => {
it('should calculate rgba values from hex and string alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('11223344', '0.5', parseRgb));
});

it('should calculate rgba values from hex and numerical alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.75)', hexToRgba('11223344', 0.75, parseRgb));
});

it('should handle the edge case where alpha value is 1', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('11223344', 1, parseRgb));
});

it('should handle the edge case where alpha value is 0', () => {
assert.equal('rgba(17, 34, 51, 0)', hexToRgba('11223344', 0, parseRgb));
});

it('should calculate rgba values from hex with leading hash and string alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('#11223344', '0.5', parseRgb));
});

it('should calculate rgba values from hex with leading hash and numerical alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.75)', hexToRgba('#11223344', 0.75, parseRgb));
});
});

describe('4-digit hex values, no a', () => {
it('should calculate correct rgb values', () => {
assert.equal('rgba(17, 34, 51, 0.27)', hexToRgba('1234', undefined, parseRgb));
});

it('should ignore a leading hash sign', () => {
assert.equal('rgba(17, 34, 51, 0.27)', hexToRgba('#1234', undefined, parseRgb));
});

it('should handle the edge case where alpha value is 1', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('123f', undefined, parseRgb));
});

it('should handle the edge case where alpha value is 0', () => {
assert.equal('rgba(17, 34, 51, 0)', hexToRgba('1230', undefined, parseRgb));
});
});

describe('4-digit hex values, a as parameter (separate parameter should override alpha in hex)', () => {
it('should calculate rgba values from hex and string alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('1234', '0.5', parseRgb));
});

it('should calculate rgba values from hex and numerical alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.75)', hexToRgba('1234', 0.75, parseRgb));
});

it('should handle the edge case where alpha value is 1', () => {
assert.equal('rgba(17, 34, 51, 1)', hexToRgba('1234', 1, parseRgb));
});

it('should handle the edge case where alpha value is 0', () => {
assert.equal('rgba(17, 34, 51, 0)', hexToRgba('1234', 0, parseRgb));
});

it('should calculate rgba values from hex with leading hash and string alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('#1234', '0.5', parseRgb));
});

it('should calculate rgba values from hex with leading hash and numerical alpha value', () => {
assert.equal('rgba(17, 34, 51, 0.75)', hexToRgba('#1234', 0.75, parseRgb));
});
});
});
});
});
104 changes: 104 additions & 0 deletions test/rgba.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* global describe it */
import assert from 'assert';
import hexToRgba from '..';

describe('rgba?-to-rgba', () => {
describe('parseRgb option', () => {
it('should parse rgba strings when set to true', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('rgba(17, 34, 51, 1)', '0.5', true));
});

it('should parse rgb strings when set to true', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('rgb(17, 34, 51)', '0.5', true));
});
});

describe('rgb parser', () => {
it('should throw when given an alpha channel', () => {
const callback = () => hexToRgba('rgb(17, 34, 51, 1)', undefined, true);
assert.throws(callback, Error); // RgbParseError doesn't pass, even though it throws
});

it('should return the rgb string if "a" is undefined', () => {
assert.equal('rgb(17, 34, 51)', hexToRgba('rgb(17, 34, 51)', undefined, true));
});

it('should accept all valid rgb values', () => {
for (let i = 0; i <= 255; i++) { // eslint-disable-line no-plusplus
const result = hexToRgba(`rgb(${i}, ${i}, ${i})`, '1', true);
assert.equal(result, `rgba(${i}, ${i}, ${i}, 1)`);
}
});

['-1', '-2', '-100', '256', '257', '1000'].forEach((val) => {
describe(`given the invalid rgb value: ${val}`, () => {
[
`rgb(${val}, 0, 0)`,
`rgb(0, ${val}, 0)`,
`rgb(0, 0, ${val})`,
`rgb(0, ${val}, ${val})`,
`rgb(${val}, ${val}, 0)`,
`rgb(${val}, 0, ${val})`,
`rgb(${val}, ${val}, ${val})`,
].forEach((rgbStr) => {
it(`should throw for: ${rgbStr}`, () => {
const callback = () => hexToRgba(rgbStr, undefined, true);
assert.throws(callback, Error); // RgbParseError doesn't pass, even though it throws
});
});
});
});
});

describe('rgba parser', () => {
it('should throw when not given an alpha channel', () => {
const callback = () => hexToRgba('rgba(17, 34, 51)', undefined, true);
assert.throws(callback, Error); // RgbParseError doesn't pass, even though it throws
});

it('should leave the alpha channel untouched if no alpha is given', () => {
assert.equal('rgba(17, 34, 51, 0.5)', hexToRgba('rgba(17, 34, 51, 0.5)', undefined, true));
});

it('should accept all valid rgb values', () => {
for (let i = 0; i <= 255; i++) { // eslint-disable-line no-plusplus
const result = hexToRgba(`rgba(${i}, ${i}, ${i}, 1)`, '1', true);
assert.equal(result, `rgba(${i}, ${i}, ${i}, 1)`);
}
});

it('should accept at least all rgb-alpha values from 0.001 to 1, when a is undefined', () => {
for (let i = 0.001; i <= 1; i += 0.001) {
const result = hexToRgba(`rgba(0, 0, 0, ${i})`, undefined, true);
assert.equal(result, `rgba(0, 0, 0, ${i})`);
}
});

it('should accept a mix of valid rgb and alpha values, when a is undefined', () => {
for (let i = 0; i <= 255; i++) { // eslint-disable-line no-plusplus
// Magic number: 1/255 = 0.00392156862745098 - allows alpha to scale with RGB values, <= 1
const result = hexToRgba(`rgba(${i}, ${i}, ${i}, ${i * 0.00392156862745098})`, undefined, true);
assert.equal(result, `rgba(${i}, ${i}, ${i}, ${i * 0.00392156862745098})`);
}
});

['-1', '-2', '-100', '256', '257', '1000'].forEach((val) => {
describe(`given the invalid rgb value: ${val}`, () => {
[
`rgba(${val}, 0, 0, 1)`,
`rgba(0, ${val}, 0, 1)`,
`rgba(0, 0, ${val}, 1)`,
`rgba(0, ${val}, ${val}, 1)`,
`rgba(${val}, ${val}, 0, 1)`,
`rgba(${val}, 0, ${val}, 1)`,
`rgba(${val}, ${val}, ${val}, 1)`,
].forEach((rgbaStr) => {
it(`should throw for: ${rgbaStr}`, () => {
const callback = () => hexToRgba(rgbaStr, undefined, true);
assert.throws(callback, Error); // RgbParseError doesn't pass, even though it throws
});
});
});
});
});
});
Loading