Skip to content

Commit 7977955

Browse files
author
Dario Soller
committed
feat: allow configuring math fraction digits and color precision on different levels (WIP)
1 parent e3abab0 commit 7977955

9 files changed

+670
-211
lines changed

src/TransformOptions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ export type ColorModifierFormat = 'hex' | 'hsl' | 'lch' | 'p3' | 'srgb';
22

33
export interface ColorModifierOptions {
44
format: ColorModifierFormat;
5+
mathFractionDigits?: number;
6+
precision?: number;
57
}
68

79
export interface TransformOptions {

src/checkAndEvaluateMath.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import { DesignToken } from 'style-dictionary/types';
22
import { Parser } from 'expr-eval-fork';
33
import { parse, reduceExpression } from '@bundled-es-modules/postcss-calc-ast-parser';
44

5-
const defaultFractionDigits = 4;
65
const mathChars = ['+', '-', '*', '/'];
7-
86
const parser = new Parser();
97

108
function checkIfInsideGroup(expr: string, fullExpr: string): boolean {
@@ -75,10 +73,7 @@ function splitMultiIntoSingleValues(expr: string): string[] {
7573
return [expr];
7674
}
7775

78-
export function parseAndReduce(
79-
expr: string,
80-
fractionDigits = defaultFractionDigits,
81-
): string | number {
76+
export function parseAndReduce(expr: string, mathFractionDigits: number): string | number {
8277
let result: string | number = expr;
8378

8479
// Check if expression is already a number
@@ -149,14 +144,14 @@ export function parseAndReduce(
149144
}
150145

151146
// the outer Number() gets rid of insignificant trailing zeros of decimal numbers
152-
const reducedToFixed = Number(Number.parseFloat(`${result}`).toFixed(fractionDigits));
147+
const reducedToFixed = Number(Number.parseFloat(`${result}`).toFixed(mathFractionDigits));
153148
result = resultUnit ? `${reducedToFixed}${resultUnit}` : reducedToFixed;
154149
return result;
155150
}
156151

157152
export function checkAndEvaluateMath(
158153
token: DesignToken,
159-
fractionDigits?: number,
154+
mathFractionDigits: number,
160155
): DesignToken['value'] {
161156
const expr = token.$value ?? token.value;
162157
const type = token.$type ?? token.type;
@@ -170,7 +165,7 @@ export function checkAndEvaluateMath(
170165
return expr;
171166
}
172167
const exprs = splitMultiIntoSingleValues(expr);
173-
const reducedExprs = exprs.map(_expr => parseAndReduce(_expr, fractionDigits));
168+
const reducedExprs = exprs.map(_expr => parseAndReduce(_expr, mathFractionDigits));
174169
if (reducedExprs.length === 1) {
175170
return reducedExprs[0];
176171
}

src/color-modifiers/mix.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ export function mix(
66
colorSpace: ColorSpaceTypes,
77
amount: number,
88
mixColor: Color,
9+
precision: number,
910
): Color {
1011
const mixValue = Math.max(0, Math.min(1, Number(amount)));
1112

12-
return new Color(color.mix(mixColor, mixValue, { space: colorSpace }).toString());
13+
return new Color(color.mix(mixColor, mixValue, { space: colorSpace }).toString({ precision }));
1314
}

src/color-modifiers/modifyColor.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ export function modifyColor(
3737
}
3838

3939
baseColor = parseUIColor(baseColor);
40-
4140
const color = new Color(baseColor);
4241
let returnedColor = color;
43-
const modifyValueResolvedCalc = Number(parseAndReduce(modifier.value, 4));
42+
const modifyValueResolvedCalc = Number(
43+
parseAndReduce(modifier.value, modifier.mathFractionDigits),
44+
);
45+
4446
try {
4547
switch (modifier.type) {
4648
case 'lighten':
@@ -55,6 +57,7 @@ export function modifyColor(
5557
modifier.space,
5658
modifyValueResolvedCalc,
5759
new Color(modifier.color),
60+
modifier.precision,
5861
);
5962
break;
6063
case 'alpha': {
@@ -77,7 +80,11 @@ export function modifyColor(
7780
}
7881
}
7982

80-
return returnedColor.toString({ inGamut: true, precision: 3, format: modifier.format });
83+
return returnedColor.toString({
84+
inGamut: true,
85+
precision: modifier.precision,
86+
format: modifier.format,
87+
});
8188
} catch (e) {
8289
return baseColor;
8390
}

src/color-modifiers/transformColorModifiers.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import { usesReferences } from 'style-dictionary/utils';
33
import { modifyColor } from './modifyColor.js';
44
import { ColorModifier } from '@tokens-studio/types';
55
import { ColorModifierOptions } from '../TransformOptions.js';
6+
7+
// 3 for backwards compatibility, but should better be 5, referring to the default precision (5) of the colorJS.io library
8+
// see: https://colorjs.io/docs/output.html#getting-a-string-representation-of-a-color
9+
const defaultColorPrecision = 3;
10+
611
/**
712
* Helper: Transforms color tokens with tokens studio color modifiers
813
*/
914
export function transformColorModifiers(
1015
token: DesignToken,
16+
mathFractionDigits: number,
1117
options?: ColorModifierOptions,
1218
): string | undefined {
1319
const modifier = token.$extensions['studio.tokens']?.modify as ColorModifier;
@@ -22,5 +28,11 @@ export function transformColorModifiers(
2228
if (options?.format) {
2329
modifier.format = options.format;
2430
}
31+
if (!modifier.mathFractionDigits) {
32+
modifier.mathFractionDigits = options?.mathFractionDigits ?? mathFractionDigits;
33+
}
34+
if (!modifier.precision) {
35+
modifier.precision = options?.precision ?? defaultColorPrecision;
36+
}
2537
return modifyColor(token.$value ?? token.value, modifier);
2638
}

src/register.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { transformOpacity } from './transformOpacity.js';
1414
import { parseTokens } from './preprocessors/parse-tokens.js';
1515
import { transformShadow } from './css/transformShadow.js';
1616

17+
const defaultFractionDigits = 4;
18+
1719
export const getTransforms = (transformOpts?: TransformOptions) => {
1820
const agnosticTransforms = [
1921
'ts/descriptionToComment',
@@ -73,7 +75,8 @@ export async function register(sd: typeof StyleDictionary, transformOpts?: Trans
7375
type: 'value',
7476
transitive: true,
7577
filter: token => ['string', 'object'].includes(typeof (token.$value ?? token.value)),
76-
transform: (token, platformCfg) => checkAndEvaluateMath(token, platformCfg.mathFractionDigits),
78+
transform: (token, platformCfg) =>
79+
checkAndEvaluateMath(token, platformCfg.mathFractionDigits ?? defaultFractionDigits),
7780
});
7881

7982
sd.registerTransform({
@@ -171,7 +174,12 @@ export async function register(sd: typeof StyleDictionary, transformOpts?: Trans
171174
(token.$type ?? token.type) === 'color' &&
172175
token.$extensions &&
173176
token.$extensions['studio.tokens']?.modify,
174-
transform: token => transformColorModifiers(token, transformOpts?.['ts/color/modifiers']),
177+
transform: (token, platformCfg) =>
178+
transformColorModifiers(
179+
token,
180+
platformCfg.mathFractionDigits ?? defaultFractionDigits,
181+
transformOpts?.['ts/color/modifiers'],
182+
),
175183
});
176184

177185
const includeBuiltinGroup = transformOpts?.withSDBuiltins ?? true;

test/integration/color-modifier-references.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,15 @@ describe('color modifier references', () => {
8888

8989
it('supports color with referenced base color, referenced mix color and reference mix value', async () => {
9090
const file = await promises.readFile(outputFilePath, 'utf-8');
91-
const content = excerpt(file, { start: new RegExp('--sdColor5: .*;'), end: '--sdColor8' });
92-
const expectedOutput = `--sdColor7: #3b64b3;`;
91+
const content = excerpt(file, { start: new RegExp('--sdColor6: .*;'), end: '--sdColor8' });
92+
const expectedOutput = `--sdColor7: #32528c;`;
9393
expect(content).toBe(expectedOutput);
9494
});
9595

9696
it('supports color with referenced base color, referenced mix color, and expression-based mix value with references', async () => {
9797
const file = await promises.readFile(outputFilePath, 'utf-8');
98-
const content = excerpt(file, { start: new RegExp('--sdColor6: .*;'), end: '}' });
98+
const content = excerpt(file, { start: new RegExp('--sdColor7: .*;'), end: '}' });
9999
const expectedOutput = `--sdColor8: #3b64b3;`;
100-
expect(content).to.equal(expectedOutput);
100+
expect(content).toBe(expectedOutput);
101101
});
102102
});

0 commit comments

Comments
 (0)