Skip to content

Commit c154597

Browse files
committed
feat(parser): new style parser * 3
1 parent e5d4739 commit c154597

File tree

6 files changed

+68
-117
lines changed

6 files changed

+68
-117
lines changed

src/parser/classify.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ export function classify(
1616
const token = raw.trim();
1717
if (!token) return { bucket: Bucket.Mod, processed: '' };
1818

19+
// 0. Direct var(--*-color) token
20+
const varColorMatch = token.match(/^var\(--([a-z0-9-]+)-color\)$/);
21+
if (varColorMatch) {
22+
return { bucket: Bucket.Color, processed: token };
23+
}
24+
1925
// 1. URL
2026
if (token.startsWith('url(')) {
2127
return { bucket: Bucket.Value, processed: token };
@@ -34,9 +40,12 @@ export function classify(
3440
}
3541
const identMatch = token.match(/^@([a-z0-9-_]+)$/);
3642
if (identMatch) {
43+
const name = identMatch[1];
44+
const processed = `var(--${name})`;
45+
const bucketType = name.endsWith('-color') ? Bucket.Color : Bucket.Value;
3746
return {
38-
bucket: Bucket.Value,
39-
processed: `var(--${identMatch[1]})`,
47+
bucket: bucketType,
48+
processed,
4049
};
4150
}
4251
// invalid custom property → modifier
@@ -60,10 +69,9 @@ export function classify(
6069
// hyphen variant e.g., #dark-05 → treat as base color
6170
const hyphenMatch = token.match(/^#([a-z0-9-]+?)-[0-9]+$/i);
6271
if (hyphenMatch) {
63-
const base = hyphenMatch[1];
6472
return {
6573
bucket: Bucket.Color,
66-
processed: `var(--${base}-color, rgb(var(--${base}-color-rgb)))`,
74+
processed: `var(--${hyphenMatch[1]}-color)`,
6775
};
6876
}
6977

@@ -76,10 +84,7 @@ export function classify(
7684
};
7785
}
7886
// simple color name token → css variable lookup with rgb fallback
79-
return {
80-
bucket: Bucket.Color,
81-
processed: `var(--${name}-color, rgb(var(--${name}-color-rgb)))`,
82-
};
87+
return { bucket: Bucket.Color, processed: `var(--${name}-color)` };
8388
}
8489

8590
// 4 & 5. Functions
@@ -158,6 +163,11 @@ export function classify(
158163
return { bucket: Bucket.Value, processed: token };
159164
}
160165

166+
// 8b. Special keyword colors
167+
if (token === 'transparent' || token === 'currentcolor') {
168+
return { bucket: Bucket.Color, processed: token };
169+
}
170+
161171
// 9. Fallback modifier
162172
return { bucket: Bucket.Mod, processed: token };
163173
}

src/parser/parser.test.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ describe('StyleProcessor', () => {
3737
);
3838
expect(result.groups[0].colors).toEqual([
3939
'var(--dark-color)',
40-
'rgb(var(--purple-color-rgb), 0)',
41-
'rgb(var(--purple-color-rgb), .5)',
42-
'rgb(var(--purple-color-rgb), .05)',
40+
'rgb(var(--purple-color-rgb) / 0)',
41+
'rgb(var(--purple-color-rgb) / .5)',
42+
'rgb(var(--purple-color-rgb) / .05)',
4343
'rgb(10,20,30)',
4444
'hsl(10,20%,30%)',
4545
]);
@@ -153,4 +153,18 @@ describe('StyleProcessor', () => {
153153
'calc(-0.5 * var(--radius))',
154154
]);
155155
});
156+
157+
test('treats custom var/@ colors as colors', () => {
158+
const res = parser.process('@clear-color var(--clear-color)');
159+
expect(res.groups[0].colors).toEqual([
160+
'var(--clear-color)',
161+
'var(--clear-color)',
162+
]);
163+
});
164+
165+
test('recognises transparent keyword as color', () => {
166+
const r = parser.process('transparent 1x');
167+
expect(r.groups[0].colors).toEqual(['transparent']);
168+
expect(r.groups[0].values).toContain('var(--gap)');
169+
});
156170
});

src/tasty/__snapshots__/tasty.test.tsx.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ exports[`tasty() API should be able to override styles 1`] = `
8484
}
8585
8686
.c0.c0 {
87-
color: rgb(var(--black-color-rgb) / 0.1);
87+
color: rgb(var(--black-color-rgb) / .1);
8888
--current-color: var(--black-color, black);
8989
--current-color-rgb: var(--black-color-rgb);
9090
}
@@ -335,7 +335,7 @@ exports[`tasty() API should pass styles from tasty 1`] = `
335335
}
336336
337337
.c0.c0 {
338-
color: rgb(var(--clear-color-rgb) / 0.1);
338+
color: rgb(var(--clear-color-rgb) / .1);
339339
--current-color: var(--clear-color, clear);
340340
--current-color-rgb: var(--clear-color-rgb);
341341
}

src/tasty/styles.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import { radiusStyle } from './styles/radius';
1111
describe('Tasty style tests', () => {
1212
it('should handle border styles', () => {
1313
expect(borderStyle({ border: '1px solid #000' })).toEqual({
14-
border: '1px solid var(--000-color, rgb(var(--000-color-rgb)))',
14+
border: '1px solid var(--000-color, #000)',
1515
});
1616
});
1717

1818
it('should handle outline styles', () => {
1919
expect(outlineStyle({ outline: '2px dashed #f00' })).toEqual({
20-
outline: '2px dashed var(--f00-color, rgb(var(--f00-color-rgb)))',
20+
outline: '2px dashed var(--f00-color, #f00)',
2121
});
2222
});
2323

src/tasty/styles/scrollbar.test.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @ts-nocheck
12
import { scrollbarStyle } from './scrollbar';
23

34
describe('scrollbarStyle', () => {
@@ -6,7 +7,7 @@ describe('scrollbarStyle', () => {
67
});
78

89
it('handles boolean true value as thin', () => {
9-
const result = scrollbarStyle({ scrollbar: true });
10+
const result: any = scrollbarStyle({ scrollbar: true })!;
1011
expect(result['scrollbar-width']).toBe('thin');
1112
});
1213

@@ -19,7 +20,7 @@ describe('scrollbarStyle', () => {
1920
it('handles "none" modifier', () => {
2021
const result = scrollbarStyle({ scrollbar: 'none' });
2122
expect(result['scrollbar-width']).toBe('none');
22-
expect(result['scrollbar-color']).toBe('transparent transparent');
23+
expect((result as any)['scrollbar-color']).toBe('transparent transparent');
2324
expect(result['&::-webkit-scrollbar']['width']).toBe('0px');
2425
});
2526

@@ -33,10 +34,10 @@ describe('scrollbarStyle', () => {
3334

3435
it('handles custom colors', () => {
3536
const result = scrollbarStyle({ scrollbar: '#red #blue #green' });
36-
expect(result['scrollbar-color']).toBe(
37+
expect((result as any)['scrollbar-color']).toBe(
3738
'var(--red-color) var(--blue-color)',
3839
);
39-
expect(result['&::-webkit-scrollbar-track']['background']).toBe(
40+
expect((result as any)['&::-webkit-scrollbar-track']['background']).toBe(
4041
'var(--blue-color)',
4142
);
4243
expect(result['&::-webkit-scrollbar-thumb']['background']).toBe(
@@ -69,13 +70,13 @@ describe('scrollbarStyle', () => {
6970
const result = scrollbarStyle({
7071
scrollbar: 'styled #purple #dark #light-grey',
7172
});
72-
expect(result['scrollbar-color']).toBe(
73+
expect((result as any)['scrollbar-color']).toBe(
7374
'var(--purple-color) var(--dark-color)',
7475
);
75-
expect(result['&::-webkit-scrollbar']['background']).toBe(
76+
expect((result as any)['&::-webkit-scrollbar']['background']).toBe(
7677
'var(--dark-color)',
7778
);
78-
expect(result['&::-webkit-scrollbar-track']['background']).toBe(
79+
expect((result as any)['&::-webkit-scrollbar-track']['background']).toBe(
7980
'var(--dark-color)',
8081
);
8182
expect(result['&::-webkit-scrollbar-thumb']['background']).toBe(
@@ -89,13 +90,13 @@ describe('scrollbarStyle', () => {
8990
it('applies partial custom colors with defaults', () => {
9091
const result = scrollbarStyle({ scrollbar: 'styled #danger' });
9192
// Only thumb color specified, track should use default
92-
expect(result['scrollbar-color']).toBe(
93+
expect((result as any)['scrollbar-color']).toBe(
9394
'var(--danger-color) var(--scrollbar-track-color, transparent)',
9495
);
9596
expect(result['&::-webkit-scrollbar-thumb']['background']).toBe(
9697
'var(--danger-color)',
9798
);
98-
expect(result['&::-webkit-scrollbar-track']['background']).toBe(
99+
expect((result as any)['&::-webkit-scrollbar-track']['background']).toBe(
99100
'var(--scrollbar-track-color, transparent)',
100101
);
101102
});

src/tasty/utils/styles.ts

Lines changed: 19 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -259,112 +259,38 @@ export function flattenStyleDetails(processed: ProcessedStyle) {
259259
* Parse color. Find it value, name and opacity.
260260
*/
261261
export function parseColor(val: string, ignoreError = false): ParsedColor {
262-
val = val.trim();
263-
262+
val = (val ?? '').trim();
264263
if (!val) return {};
265264

266-
if (val.startsWith('#')) {
267-
val = val.slice(1);
268-
269-
const tmp = val.split('.');
270-
271-
let opacity = 100;
272-
273-
if (tmp.length > 1) {
274-
if (tmp[1].length === 1) {
275-
opacity = Number(tmp[1]) * 10;
276-
} else {
277-
opacity = Number(tmp[1]);
278-
}
279-
280-
if (Number.isNaN(opacity)) {
281-
opacity = 100;
282-
}
283-
}
284-
285-
const name = tmp[0];
265+
// Utilize the new parser to extract the first color token.
266+
const processed = parseStyle(val as any);
267+
const firstColor = processed.groups.find((g) => g.colors.length)?.colors[0];
286268

287-
let color;
288-
289-
if (name === 'current') {
290-
color = 'currentColor';
291-
} else {
292-
if (opacity > 100) {
293-
opacity = 100;
294-
} else if (opacity < 0) {
295-
opacity = 0;
296-
}
297-
}
298-
299-
if (!color) {
300-
color =
301-
opacity !== 100
302-
? rgbColorProp(name, Math.round(opacity) / 100)
303-
: colorProp(name, null, strToRgb(`#${name}`));
304-
}
305-
306-
return {
307-
color,
308-
name,
309-
opacity: opacity != null ? opacity : 100,
310-
};
311-
}
312-
313-
const flat = flattenStyleDetails(parseStyle(val));
314-
let { values, mods, color } = flat;
315-
316-
let name, opacity;
317-
318-
if (color) {
319-
return {
320-
color: (!color.startsWith('var(') ? strToRgb(color) : color) || color,
321-
};
322-
}
323-
324-
values.forEach((token) => {
325-
if (token.match(/^((var|rgb|rgba|hsl|hsla)\(|#[0-9a-f]{3,6})/)) {
326-
color = !token.startsWith('var') ? strToRgb(token) : token;
327-
} else if (token.endsWith('%')) {
328-
opacity = parseInt(token);
329-
}
330-
});
331-
332-
if (color) {
333-
return { color };
334-
}
335-
336-
name = name || mods[0];
337-
338-
if (!name) {
269+
if (!firstColor) {
339270
if (!ignoreError && devMode) {
340-
console.warn('CubeUIKit: incorrect color value:', val);
271+
console.warn('CubeUIKit: unable to parse color:', val);
341272
}
342-
343273
return {};
344274
}
345275

346-
if (!opacity) {
347-
let color;
276+
// Extract color name (if present) from variable pattern.
277+
let nameMatch = firstColor.match(/var\(--([a-z0-9-]+)-color/);
278+
if (!nameMatch) {
279+
nameMatch = firstColor.match(/var\(--([a-z0-9-]+)-color-rgb/);
280+
}
348281

349-
if (name === 'current') {
350-
color = 'currentColor';
351-
} else if (name === 'inherit') {
352-
color = 'inherit';
353-
} else if (name !== 'transparent' && name !== 'currentColor') {
354-
color = `var(--${name}-color, ${name})`;
355-
} else {
356-
color = name;
282+
let opacity: number | undefined;
283+
if (firstColor.startsWith('rgb')) {
284+
const alphaMatch = firstColor.match(/\/\s*([0-9.]+)\)/);
285+
if (alphaMatch) {
286+
const v = parseFloat(alphaMatch[1]);
287+
if (!isNaN(v)) opacity = v * 100;
357288
}
358-
359-
return {
360-
name,
361-
color,
362-
};
363289
}
364290

365291
return {
366-
color: rgbColorProp(name, Math.round(opacity) / 100),
367-
name,
292+
color: firstColor,
293+
name: nameMatch ? nameMatch[1] : undefined,
368294
opacity,
369295
};
370296
}

0 commit comments

Comments
 (0)