Skip to content

Commit 195ace9

Browse files
committed
feat: parse positive numeric
Required for eg. <shape-radius> circle() argument, used to define clip-path, shape-outside, or offset-path.
1 parent 852e70a commit 195ace9

File tree

2 files changed

+40
-9
lines changed

2 files changed

+40
-9
lines changed

lib/parsers.js

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ function serializeNumber(n) {
156156
* https://drafts.csswg.org/css-values-4/#integers
157157
* https://drafts.csswg.org/cssom/#ref-for-integer-value
158158
*/
159-
exports.parseInteger = function parseInteger(val) {
159+
exports.parseInteger = function parseInteger(val, positive = false) {
160160
if (val === '') {
161161
return val;
162162
}
@@ -167,7 +167,13 @@ exports.parseInteger = function parseInteger(val) {
167167
const res = numberRegEx.exec(val);
168168
if (res) {
169169
const [, , integer, decimals, onlyDecimals, exponent] = res;
170-
if (integer === undefined || decimals || onlyDecimals || (exponent && exponent.slice(1) < 0)) {
170+
if (
171+
integer === undefined ||
172+
decimals ||
173+
onlyDecimals ||
174+
(exponent && exponent.slice(1) < 0) ||
175+
(positive && integer < 0)
176+
) {
171177
return undefined;
172178
}
173179
return serializeNumber(val);
@@ -179,7 +185,7 @@ exports.parseInteger = function parseInteger(val) {
179185
* https://drafts.csswg.org/css-values-4/#numbers
180186
* https://drafts.csswg.org/cssom/#ref-for-number-value
181187
*/
182-
exports.parseNumber = function parseNumber(val) {
188+
exports.parseNumber = function parseNumber(val, positive = false) {
183189
if (val === '') {
184190
return val;
185191
}
@@ -188,6 +194,9 @@ exports.parseNumber = function parseNumber(val) {
188194
val = calculated;
189195
}
190196
if (numberRegEx.test(val)) {
197+
if (positive && val < 0) {
198+
return undefined;
199+
}
191200
return serializeNumber(val);
192201
}
193202
return exports.parseCustomVariable(val);
@@ -197,14 +206,14 @@ exports.parseNumber = function parseNumber(val) {
197206
* https://drafts.csswg.org/css-values-4/#lengths
198207
* https://drafts.csswg.org/cssom/#ref-for-length-value
199208
*/
200-
exports.parseLength = function parseLength(val, resolve = false) {
209+
exports.parseLength = function parseLength(val, resolve = false, positive = false) {
201210
if (val === '') {
202211
return val;
203212
}
204213
if (val === '0') {
205214
return '0px';
206215
}
207-
const calculated = exports.parseCalc(val, v => parseLength(v, resolve));
216+
const calculated = exports.parseCalc(val, v => parseLength(v, resolve, positive));
208217
if (calculated) {
209218
if (!resolve) {
210219
return calculated;
@@ -214,6 +223,9 @@ exports.parseLength = function parseLength(val, resolve = false) {
214223
const res = lengthRegEx.exec(val);
215224
if (res) {
216225
let [, number, , , , , , unit] = res;
226+
if (positive && number < 0) {
227+
return undefined;
228+
}
217229
unit = unit.toLowerCase();
218230
if (resolve) {
219231
switch (unit) {
@@ -252,14 +264,14 @@ exports.parseLength = function parseLength(val, resolve = false) {
252264
* https://drafts.csswg.org/css-values-4/#percentages
253265
* https://drafts.csswg.org/cssom/#ref-for-percentage-value
254266
*/
255-
exports.parsePercentage = function parsePercentage(val, resolve = false) {
267+
exports.parsePercentage = function parsePercentage(val, resolve = false, positive = false) {
256268
if (val === '') {
257269
return val;
258270
}
259271
if (val === '0') {
260272
return '0%';
261273
}
262-
const calculated = exports.parseCalc(val, v => parsePercentage(v, resolve));
274+
const calculated = exports.parseCalc(val, v => parsePercentage(v, resolve, positive));
263275
if (calculated) {
264276
if (!resolve) {
265277
return calculated;
@@ -269,13 +281,18 @@ exports.parsePercentage = function parsePercentage(val, resolve = false) {
269281
const res = percentageRegEx.exec(val);
270282
if (res) {
271283
const [, number] = res;
284+
if (positive && number < 0) {
285+
return undefined;
286+
}
272287
return serializeNumber(number) + '%';
273288
}
274289
return exports.parseCustomVariable(val);
275290
};
276291

277-
exports.parseLengthOrPercentage = function parseLengthOrPercentage(val, resolve) {
278-
return exports.parseLength(val, resolve) || exports.parsePercentage(val, resolve);
292+
exports.parseLengthOrPercentage = function parseLengthOrPercentage(val, resolve, positive) {
293+
return (
294+
exports.parseLength(val, resolve, positive) || exports.parsePercentage(val, resolve, positive)
295+
);
279296
};
280297

281298
/**

lib/parsers.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ describe('parseInteger', () => {
77
const invalid = ['string', '1px', '#1', '1.1', '.1', '1e-1', 'calc(1 / 2)'];
88
invalid.forEach(input => expect(parsers.parseInteger(input)).toBeUndefined());
99
});
10+
it('returns undefined for negative values when positive is required', () => {
11+
expect(parsers.parseInteger(-1, true)).toBeUndefined();
12+
});
1013
it('parses integer with exponent', () => {
1114
expect(parsers.parseInteger('1e1')).toBe('10');
1215
expect(parsers.parseInteger('1e+1')).toBe('10');
@@ -23,6 +26,9 @@ describe('parseNumber', () => {
2326
const invalid = ['string', '1px', '#1', 'calc(1 * 1px)'];
2427
invalid.forEach(input => expect(parsers.parseNumber(input)).toBeUndefined());
2528
});
29+
it('returns undefined for negative values when positive is required', () => {
30+
expect(parsers.parseNumber(-1, true)).toBeUndefined();
31+
});
2632
it('parses number with exponent', () => {
2733
expect(parsers.parseNumber('1e1')).toBe('10');
2834
expect(parsers.parseNumber('1e+1')).toBe('10');
@@ -46,6 +52,10 @@ describe('parseLength', () => {
4652
const invalid = ['string', '1', 'px', '1%', '#1px', '1px%', 'calc(1 * 1%)'];
4753
invalid.forEach(input => expect(parsers.parseLength(input)).toBeUndefined());
4854
});
55+
it('returns undefined for negative values when positive is required', () => {
56+
expect(parsers.parseLength('-1px', false, true)).toBeUndefined();
57+
expect(parsers.parsePercentage('calc(-1px)', true, true)).toBeUndefined();
58+
});
4959
it('parses 0 to 0px', () => {
5060
expect(parsers.parseLength('0')).toBe('0px');
5161
});
@@ -84,6 +94,10 @@ describe('parsePercentage', () => {
8494
const invalid = ['string', '1', '%', '1px', '#1%', '1%%', 'calc(1 * 1px)'];
8595
invalid.forEach(input => expect(parsers.parsePercentage(input)).toBeUndefined());
8696
});
97+
it('returns undefined for negative values when positive is required', () => {
98+
expect(parsers.parsePercentage('-1%', false, true)).toBeUndefined();
99+
expect(parsers.parsePercentage('calc(-1%)', true, true)).toBeUndefined();
100+
});
87101
it('parses percentage with exponent', () => {
88102
expect(parsers.parsePercentage('1e1%')).toBe('10%');
89103
expect(parsers.parsePercentage('1e+1%')).toBe('10%');

0 commit comments

Comments
 (0)