Skip to content

Commit

Permalink
feat: color gradient
Browse files Browse the repository at this point in the history
  • Loading branch information
cdoublev committed May 13, 2021
1 parent ab76b79 commit 363fd1d
Show file tree
Hide file tree
Showing 16 changed files with 696 additions and 54 deletions.
460 changes: 448 additions & 12 deletions lib/parsers.js

Large diffs are not rendered by default.

225 changes: 214 additions & 11 deletions lib/parsers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,29 @@ describe('parseLength', () => {
expect(parsers.parseLength('calc(1px + 1px)')).toBe('calc(2px)');
});
});
describe('parsePercent', () => {
describe('parsePercentage', () => {
it('returns undefined for invalid values', () => {
const invalid = ['string', '1%%', '1px%', '#1%', 'calc(1 * 1px)'];
invalid.forEach(input => expect(parsers.parsePercent(input)).toBeUndefined());
invalid.forEach(input => expect(parsers.parsePercentage(input)).toBeUndefined());
});
it('parses percent with exponent', () => {
expect(parsers.parsePercent('1e1%')).toBe('10%');
expect(parsers.parsePercent('1e+1%')).toBe('10%');
expect(parsers.parsePercent('1e-1%')).toBe('0.1%');
expect(parsers.parsePercentage('1e1%')).toBe('10%');
expect(parsers.parsePercentage('1e+1%')).toBe('10%');
expect(parsers.parsePercentage('1e-1%')).toBe('0.1%');
});
it('parses percent with missing leading 0', () => {
expect(parsers.parsePercent('.1%')).toBe('0.1%');
expect(parsers.parsePercentage('.1%')).toBe('0.1%');
});
it('returns percent without trailing 0 in decimals', () => {
expect(parsers.parsePercent('0.10%')).toBe('0.1%');
expect(parsers.parsePercentage('0.10%')).toBe('0.1%');
});
it('works with calc', () => {
expect(parsers.parsePercent('calc(1% + 1%)')).toBe('calc(2%)');
expect(parsers.parsePercentage('calc(1% + 1%)')).toBe('calc(2%)');
});
});
describe('parseLengthOrPercentage', () => {
it.todo('test');
});
describe('parseAlpha', () => {
it('returns undefined for invalid values', () => {
const invalid = ['string', '1%%', '1px%', '#1%', 'calc(1 * 1px)'];
Expand Down Expand Up @@ -124,9 +127,6 @@ describe('parseAlpha', () => {
expect(parsers.parseAlpha('calc(0.5 + 0.5)')).toBe('1');
});
});
describe('parseMeasurement', () => {
it.todo('test');
});
describe('parseAngle', () => {
it('returns undefined for invalid values', () => {
const invalid = ['string', '1', '1degg', 'a1deg', 'deg', 'calc(1 * 1px)'];
Expand Down Expand Up @@ -251,12 +251,46 @@ describe('parseCustomIdentifier', () => {
expect(parsers.parseCustomIdentifier('myCustomIdentifier')).toBe('myCustomIdentifier');
});
});
describe('parseImage', () => {
it.todo('tests');
});
describe('parseUrl', () => {
it.todo('test');
});
describe('parseString', () => {
it.todo('test');
});
describe('parsePosition', () => {
it('returns undefined for invalid values', () => {
const invalid = [
'side',
'1',
'left left',
'top top',
'left top center',
'left top 50%',
'0% 0% 0%',
'top 50%',
'50% left',
];
invalid.forEach(input => expect(parsers.parsePosition(input)).toBeUndefined());
});
it('resolves 0 as 0px', () => {
expect(parsers.parsePosition('0 0')).toBe('0px 0px');
});
it('resolves with center as a default value', () => {
expect(parsers.parsePosition('0%')).toBe('0% center');
expect(parsers.parsePosition('left')).toBe('left center');
expect(parsers.parsePosition('top')).toBe('center top');
expect(parsers.parsePosition('center')).toBe('center center');
});
it('resolves with horizontal position first', () => {
expect(parsers.parsePosition('top left')).toBe('left top');
});
it('resolves with lowercased position', () => {
expect(parsers.parsePosition('LEFt 0%')).toBe('left 0%');
});
});
describe('parseColor', () => {
it('returns undefined for invalid values', () => {
const invalid = [
Expand Down Expand Up @@ -331,6 +365,175 @@ describe('parseColor', () => {
);
});
});
describe('parseGradient', () => {
it('returns undefined for invalid values', () => {
const invalid = [
'string',
'1',
'invalid-gradient(red, cyan)',
'invalid-conic-gradient(red, cyan)',
'conic-gradient()',
'conic-gradient( , red, cyan)',
'conic-gradient(0, cyan)',
'conic-gradient(from , red, cyan)',
'conic-gradient(from 1, red, cyan)',
'conic-gradient(from 90deg 120deg, red, cyan)',
'conic-gradient(at , red, cyan)',
'conic-gradient(at 1, red, cyan)',
'conic-gradient(at left center center, red, cyan)',
'conic-gradient(red, 0%, 0%, cyan)',
'conic-gradient(cyan 0deg)',
'linear-gradient(0, cyan)',
'linear-gradient(1, red, cyan)',
'linear-gradient(90deg 120deg, red, cyan)',
'linear-gradient(at , red, cyan)',
'linear-gradient(at 1, red, cyan)',
'linear-gradient(at left center center, red, cyan)',
'linear-gradient(red, 0%, 0%, cyan)',
'linear-gradient(cyan 0%)',
'radial-gradient(0, cyan)',
'radial-gradient(1, red, cyan)',
'radial-gradient(circle 50%, red, cyan)',
'radial-gradient(circle 100px 120px, red, cyan)',
'radial-gradient(ellipse 50%, red, cyan)',
'radial-gradient(50% closest-corner, red, cyan)',
'radial-gradient(closest-corner 50%, red, cyan)',
'radial-gradient(at , red, cyan)',
'radial-gradient(at 1, red, cyan)',
'radial-gradient(at left center center, red, cyan)',
'radial-gradient(red, 0%, 0%, cyan)',
'radial-gradient(cyan 0%)',
];
invalid.forEach(input => expect(parsers.parseGradient(input)).toBeUndefined());
});
it('parses a conic gradient', () => {
[
// [input, expected]
['conic-gradient(red, cyan)', 'conic-gradient(red, cyan)'],
['CONIC-gradient(red, cyan)', 'conic-gradient(red, cyan)'],
['repeating-conic-gradient(red, cyan)', 'repeating-conic-gradient(red, cyan)'],
['conic-gradient(from 0, red, cyan)', 'conic-gradient(from 0deg, red, cyan)'],
['conic-gradient(from 2turn, red, cyan)', 'conic-gradient(from 2turn, red, cyan)'],
['conic-gradient(at top, red, cyan)', 'conic-gradient(at center top, red, cyan)'],
['conic-gradient(at left, red, cyan)', 'conic-gradient(at left center, red, cyan)'],
['conic-gradient(at top left, red, cyan)', 'conic-gradient(at left top, red, cyan)'],
['conic-gradient(at 0%, red, cyan)', 'conic-gradient(at 0% center, red, cyan)'],
['conic-gradient(at -100% 200%, red, cyan)', 'conic-gradient(at -100% 200%, red, cyan)'],
[
'conic-gradient(from 0deg at left center, red, cyan)',
'conic-gradient(from 0deg at left center, red, cyan)',
],
['conic-gradient(red, 50%, cyan)', 'conic-gradient(red, 50%, cyan)'],
['conic-gradient(red 0 0, 0, cyan)', 'conic-gradient(red 0deg, red 0deg, 0deg, cyan)'],
[
'conic-gradient(red 0deg 1turn, 50%, cyan)',
'conic-gradient(red 0deg, red 1turn, 50%, cyan)',
],
[
'conic-gradient(red -1% 200%, 540deg, cyan)',
'conic-gradient(red -1%, red 200%, 540deg, cyan)',
],
['conic-gradient(red 0deg 180deg)', 'conic-gradient(red 0deg, red 180deg)'],
].forEach(([input, expected]) => expect(parsers.parseGradient(input)).toBe(expected));
});
it('parses a linear gradient', () => {
[
// [input, expected]
['linear-gradient(red, cyan)', 'linear-gradient(red, cyan)'],
['repeating-linear-gradient(red, cyan)', 'repeating-linear-gradient(red, cyan)'],
['linear-gradient(0, red, cyan)', 'linear-gradient(0deg, red, cyan)'],
['linear-gradient(2turn, red, cyan)', 'linear-gradient(2turn, red, cyan)'],
['linear-gradient(to top, red, cyan)', 'linear-gradient(to top, red, cyan)'],
['linear-gradient(to left, red, cyan)', 'linear-gradient(to left, red, cyan)'],
['linear-gradient(to top left, red, cyan)', 'linear-gradient(to left top, red, cyan)'],
['linear-gradient(to bottom, red, cyan)', 'linear-gradient(red, cyan)'],
['linear-gradient(to left bottom, red, cyan)', 'linear-gradient(to left bottom, red, cyan)'],
['linear-gradient(to -100% 200%, red, cyan)', 'linear-gradient(to -100% 200%, red, cyan)'],
['linear-gradient(red, 50%, cyan)', 'linear-gradient(red, 50%, cyan)'],
['linear-gradient(red 0 0, 0, cyan)', 'linear-gradient(red 0px, red 0px, 0px, cyan)'],
[
'linear-gradient(red 0px 100%, 50px, cyan)',
'linear-gradient(red 0px, red 100%, 50px, cyan)',
],
[
'linear-gradient(red -1% 200px, 150%, cyan)',
'linear-gradient(red -1%, red 200px, 150%, cyan)',
],
['linear-gradient(red 0% 50%)', 'linear-gradient(red 0%, red 50%)'],
].forEach(([input, expected]) => expect(parsers.parseGradient(input)).toBe(expected));
});
it('parses a radial gradient', () => {
[
// [input, expected]
['radial-gradient(red, cyan)', 'radial-gradient(red, cyan)'],
['repeating-radial-gradient(red, cyan)', 'repeating-radial-gradient(red, cyan)'],
['radial-gradient(circle, red, cyan)', 'radial-gradient(circle, red, cyan)'],
['radial-gradient(circle 0, red, cyan)', 'radial-gradient(0px, red, cyan)'],
['radial-gradient(circle 50px, red, cyan)', 'radial-gradient(50px, red, cyan)'],
['radial-gradient(50px circle, red, cyan)', 'radial-gradient(50px, red, cyan)'],
['radial-gradient(circle farthest-corner, red, cyan)', 'radial-gradient(circle, red, cyan)'],
['radial-gradient(farthest-corner circle, red, cyan)', 'radial-gradient(circle, red, cyan)'],
[
'radial-gradient(circle farthest-side, red, cyan)',
'radial-gradient(circle farthest-side, red, cyan)',
],
[
'radial-gradient(farthest-side circle, red, cyan)',
'radial-gradient(circle farthest-side, red, cyan)',
],
['radial-gradient(ellipse, red, cyan)', 'radial-gradient(red, cyan)'],
['radial-gradient(ellipse 0 120%, red, cyan)', 'radial-gradient(0px 120%, red, cyan)'],
['radial-gradient(ellipse 80% 100%, red, cyan)', 'radial-gradient(80% 100%, red, cyan)'],
['radial-gradient(80% 100% ellipse, red, cyan)', 'radial-gradient(80% 100%, red, cyan)'],
['radial-gradient(ellipse farthest-corner, red, cyan)', 'radial-gradient(red, cyan)'],
['radial-gradient(farthest-corner ellipse, red, cyan)', 'radial-gradient(red, cyan)'],
[
'radial-gradient(ellipse farthest-side, red, cyan)',
'radial-gradient(farthest-side, red, cyan)',
],
[
'radial-gradient(farthest-side ellipse, red, cyan)',
'radial-gradient(farthest-side, red, cyan)',
],
['radial-gradient(at top, red, cyan)', 'radial-gradient(at center top, red, cyan)'],
['radial-gradient(at left, red, cyan)', 'radial-gradient(at left center, red, cyan)'],
['radial-gradient(at top left, red, cyan)', 'radial-gradient(at left top, red, cyan)'],
['radial-gradient(at -100% 200%, red, cyan)', 'radial-gradient(at -100% 200%, red, cyan)'],
[
'radial-gradient(circle closest-side at center center, red, cyan)',
'radial-gradient(circle closest-side at center center, red, cyan)',
],
['radial-gradient(red, 50%, cyan)', 'radial-gradient(red, 50%, cyan)'],
['radial-gradient(red 0 0, 0, cyan)', 'radial-gradient(red 0px, red 0px, 0px, cyan)'],
[
'radial-gradient(red 0px 100%, 50px, cyan)',
'radial-gradient(red 0px, red 100%, 50px, cyan)',
],
[
'radial-gradient(red -1% 200px, 150%, cyan)',
'radial-gradient(red -1%, red 200px, 150%, cyan)',
],
['radial-gradient(red 0% 50%)', 'radial-gradient(red 0%, red 50%)'],
].forEach(([input, expected]) => expect(parsers.parseGradient(input)).toBe(expected));
});
it('works with calc', () => {
[
// [input, expected]
[
'conic-gradient(from calc(90deg * 2) at calc(25% * 2), red calc(5% * 2) calc(50% * 2), calc(25% * 2), cyan)',
'conic-gradient(from calc(180deg) at calc(50%) center, red calc(10%), red calc(100%), calc(50%), cyan)',
],
[
'linear-gradient(calc(90deg * 2), red calc(5% * 2) calc(50% * 2), calc(25% * 2), cyan)',
'linear-gradient(red calc(10%), red calc(100%), calc(50%), cyan)',
],
[
'radial-gradient(calc(25px * 2) at calc(25% * 2), red calc(5% * 2) calc(50% * 2), calc(25% * 2), cyan)',
'radial-gradient(calc(50px) at calc(50%) center, red calc(10%), red calc(100%), calc(50%), cyan)',
],
].forEach(([input, expected]) => expect(parsers.parseGradient(input)).toBe(expected));
});
});
describe('dashedToCamelCase', () => {
it.todo('test');
});
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/backgroundImage.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';

const { parseKeyword, parseUrl } = require('../parsers');
const { parseImage, parseKeyword } = require('../parsers');

function parse(v) {
return parseUrl(v) || parseKeyword(v, ['none']);
return parseImage(v) || parseKeyword(v, ['none']);
}

module.exports.isValid = function isValid(v) {
Expand Down
9 changes: 6 additions & 3 deletions lib/properties/backgroundPosition.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const { parseKeyword, parseMeasurement } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage } = require('../parsers');

var valid_keywords = ['top', 'center', 'bottom', 'left', 'right'];

Expand All @@ -13,9 +13,12 @@ var parse = function parse(v) {
return undefined;
}
if (parts.length === 1) {
return parseMeasurement(parts[0]) || parseKeyword(parts[0], valid_keywords);
return parseLengthOrPercentage(parts[0]) || parseKeyword(parts[0], valid_keywords);
}
if (parseMeasurement(parts[0]) !== undefined && parseMeasurement(parts[1]) !== undefined) {
if (
parseLengthOrPercentage(parts[0]) !== undefined &&
parseLengthOrPercentage(parts[1]) !== undefined
) {
return v;
}
if (
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/bottom.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
var parseLengthOrPercentage = require('../parsers').parseLengthOrPercentage;

module.exports.definition = {
set: function(v) {
this._setProperty('bottom', parseMeasurement(v));
this._setProperty('bottom', parseLengthOrPercentage(v));
},
get: function() {
return this.getPropertyValue('bottom');
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/clip.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
var parseLengthOrPercentage = require('../parsers').parseLengthOrPercentage;

var shape_regex = /^rect\((.*)\)$/i;

Expand All @@ -21,7 +21,7 @@ var parse = function(val) {
return undefined;
}
var valid = parts.every(function(part, index) {
var measurement = parseMeasurement(part);
var measurement = parseLengthOrPercentage(part);
parts[index] = measurement;
return measurement !== undefined;
});
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/flexBasis.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
var parseLengthOrPercentage = require('../parsers').parseLengthOrPercentage;

function parse(v) {
if (v.toLowerCase() === 'auto') {
Expand All @@ -9,7 +9,7 @@ function parse(v) {
if (v.toLowerCase() === 'inherit') {
return 'inherit';
}
return parseMeasurement(v);
return parseLengthOrPercentage(v);
}

module.exports.isValid = function isValid(v) {
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/fontSize.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
'use strict';

const { parseKeyword, parseMeasurement } = require('../parsers');
const { parseKeyword, parseLengthOrPercentage } = require('../parsers');

var absoluteSizes = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
var relativeSizes = ['larger', 'smaller'];
const allSizes = absoluteSizes.concat(relativeSizes);

function parse(v) {
return parseKeyword(v, allSizes) || parseMeasurement(v);
return parseKeyword(v, allSizes) || parseLengthOrPercentage(v);
}

module.exports.isValid = function(v) {
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/height.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
var parseLengthOrPercentage = require('../parsers').parseLengthOrPercentage;

function parse(v) {
if (v.toLowerCase() === 'auto') {
Expand All @@ -9,7 +9,7 @@ function parse(v) {
if (v.toLowerCase() === 'inherit') {
return 'inherit';
}
return parseMeasurement(v);
return parseLengthOrPercentage(v);
}

module.exports.definition = {
Expand Down
4 changes: 2 additions & 2 deletions lib/properties/left.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

var parseMeasurement = require('../parsers').parseMeasurement;
var parseLengthOrPercentage = require('../parsers').parseLengthOrPercentage;

module.exports.definition = {
set: function(v) {
this._setProperty('left', parseMeasurement(v));
this._setProperty('left', parseLengthOrPercentage(v));
},
get: function() {
return this.getPropertyValue('left');
Expand Down
Loading

0 comments on commit 363fd1d

Please sign in to comment.