Skip to content
Merged
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
38 changes: 35 additions & 3 deletions src/js/relative-color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
const {
CloseParen: PAREN_CLOSE,
Comment: COMMENT,
Delim: DELIM,
Dimension: DIM,
EOF,
Function: FUNC,
Expand Down Expand Up @@ -76,6 +77,7 @@ const REG_COLOR_CAPT = new RegExp(
);
const REG_CS_HSL = /(?:hsla?|hwb)$/;
const REG_CS_CIE = new RegExp(`^(?:${CS_LAB}|${CS_LCH})$`);
const REG_FN_CALC_SUM = /^(?:abs|sign|sin|cos|tan)\(/;
const REG_FN_MATH_START = new RegExp(SYN_FN_MATH_START);
const REG_FN_REL = new RegExp(FN_REL);
const REG_FN_REL_CAPT = new RegExp(`^${FN_REL_CAPT}`);
Expand Down Expand Up @@ -122,7 +124,8 @@ export function resolveColorChannels(
] = [[], [], [], []];
let i = 0;
let nest = 0;
let func = false;
let func = '';
let precededPct = false;
while (tokens.length) {
const token = tokens.shift();
if (!Array.isArray(token)) {
Expand All @@ -138,7 +141,24 @@ export function resolveColorChannels(
const channel = channels[i];
if (Array.isArray(channel)) {
switch (type) {
case DELIM: {
if (func) {
if (
(value === '+' || value === '-') &&
precededPct &&
!REG_FN_CALC_SUM.test(func)
) {
return new NullObject();
}
precededPct = false;
channel.push(value);
}
break;
}
case DIM: {
if (!func || !REG_FN_CALC_SUM.test(func)) {
return new NullObject();
}
const resolvedValue = resolveDimension(token, opt);
if (isString(resolvedValue)) {
channel.push(resolvedValue);
Expand All @@ -149,7 +169,7 @@ export function resolveColorChannels(
}
case FUNC: {
channel.push(value);
func = true;
func = value;
nest++;
if (REG_FN_MATH_START.test(value)) {
mathFunc.add(nest);
Expand Down Expand Up @@ -192,13 +212,25 @@ export function resolveColorChannels(
}
nest--;
if (nest === 0) {
func = false;
func = '';
i++;
}
}
break;
}
case PCT: {
if (!func) {
return new NullObject();
} else if (!REG_FN_CALC_SUM.test(func)) {
const lastValue = channel.toReversed().find(v => v !== ' ');
if (lastValue === '+' || lastValue === '-') {
return new NullObject();
} else if (lastValue === '*' || lastValue === '/') {
precededPct = false;
} else {
precededPct = true;
}
}
channel.push(Number(detail?.value) / MAX_PCT);
if (!func) {
i++;
Expand Down
65 changes: 63 additions & 2 deletions test/relative-color.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ describe('resolve relative color channels', () => {
assert.deepEqual(res, ['r', 'calc(1 * g)', 'abs(-10)'], 'result');
});

it('should get value', () => {
it('should get null object', () => {
const css = ' r calc(g * sign(2em)) 1000%)';
const tokens = tokenize({ css });
const res = func(tokens, {
colorSpace: 'rgb',
format: 'specifiedValue'
});
assert.deepEqual(res, ['r', 'calc(1 * g)', 10], 'result');
assert.deepEqual(res.isNull, true, 'result');
});

it('should get value', () => {
Expand Down Expand Up @@ -325,6 +325,67 @@ describe('extract origin color', () => {
});
assert.strictEqual(res, 'rgb(from rgb(0, 128, 0) r g b)', 'result');
});

it('should get value', () => {
const res = func('rgb(from rebeccapurple calc(r * 2) g b)', {
format: 'specifiedValue'
});
assert.strictEqual(
res,
'rgb(from rebeccapurple calc(2 * r) g b)',
'result'
);
});

it('should get value', () => {
const res = func('rgb(from rebeccapurple calc(r * 50%) g b)', {
format: 'specifiedValue'
});
assert.strictEqual(
res,
'rgb(from rebeccapurple calc(0.5 * r) g b)',
'result'
);
});

it('should get value', () => {
const res = func('rgb(from rebeccapurple calc(r * sign(50%)) g b)', {
format: 'specifiedValue'
});
assert.strictEqual(
res,
'rgb(from rebeccapurple calc(1 * r) g b)',
'result'
);
});

it('should get null object', () => {
const res = func('rgb(from rebeccapurple calc(r * 2rem) g b)', {
format: 'specifiedValue'
});
assert.strictEqual(res.isNull, true, 'result');
});

it('should get null object', () => {
const res = func('rgb(from rebeccapurple calc(r + 50%) g b)', {
format: 'specifiedValue'
});
assert.strictEqual(res.isNull, true, 'result');
});

it('should get null object', () => {
const res = func('rgb(from rebeccapurple calc(2rem * r) g b)', {
format: 'specifiedValue'
});
assert.strictEqual(res.isNull, true, 'result');
});

it('should get null object', () => {
const res = func('rgb(from rebeccapurple calc(50% + r) g b)', {
format: 'specifiedValue'
});
assert.strictEqual(res.isNull, true, 'result');
});
});

describe('resolve relative color', () => {
Expand Down