From 4624626b6feaf3d842f9164769c814fd5bede8de Mon Sep 17 00:00:00 2001 From: "s.peshkov" Date: Tue, 24 Sep 2024 18:30:34 +0300 Subject: [PATCH] feat/tokenized-gradients: fix gradient token helper --- .../helpers/getGradientPointsFromColor.ts | 4 +- src/build/helpers/tokenHelpers.test.ts | 45 ++++++++++++++++--- src/build/helpers/tokenHelpers.ts | 32 +++++++------ 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/build/helpers/getGradientPointsFromColor.ts b/src/build/helpers/getGradientPointsFromColor.ts index cf3ad10d..1ab86ff7 100644 --- a/src/build/helpers/getGradientPointsFromColor.ts +++ b/src/build/helpers/getGradientPointsFromColor.ts @@ -41,12 +41,12 @@ export function getGradientPointsFromColor( ): GradientPoints { const gradientPointData = typeof colorArg === 'function' ? colorArg() : { value: colorArg }; - const colorRGB: string = color(gradientPointData.value).rgb().array().join(', '); + const colorRGB: string = color(gradientPointData.value).rgb().array().slice(0, 3).join(', '); const colorAlpha = color(gradientPointData.value).alpha(); return opacityPoints .map(([pointOpacity, pointCoordinate]) => { - const targetOpacity = colorAlpha < 1 ? colorAlpha : pointOpacity; + const targetOpacity = colorAlpha * pointOpacity; const colorValue = `rgba(${colorRGB}, ${Math.round(targetOpacity * opacityMultiplier * 1000) / 1000})`; if (typeof gradientPointData.key === 'string') { diff --git a/src/build/helpers/tokenHelpers.test.ts b/src/build/helpers/tokenHelpers.test.ts index 07e41a9f..68b29017 100644 --- a/src/build/helpers/tokenHelpers.test.ts +++ b/src/build/helpers/tokenHelpers.test.ts @@ -35,18 +35,53 @@ describe('tokenHelpers', () => { }); describe('gradient', () => { - test('calculates gradient string', () => { + test('calculates gradient string from 1 color with variable', () => { + const gradientToken = gradient(['blue']); + const gradientValue = gradientToken({}); + + expect(gradientValue).toEqual( + [ + 'rgba(0, 0, 255, 0) 0%', + 'rgba(0, 0, 255, 0.05) 15%', + 'rgba(0, 0, 255, 0.2) 30%', + 'rgba(0, 0, 255, 0.8) 70%', + 'rgba(0, 0, 255, 0.95) 85%', + 'rgba(0, 0, 255, 1) 100%', + ].join(', '), + ); + }); + + test('calculates gradient string from 1 color with variable', () => { + const gradientToken = gradient([namedAlias('colorIconPrimary')]); + const gradientValue = gradientToken({ colors: { colorIconPrimary: 'blue' } as any }); + + expect(gradientValue).toEqual( + [ + 'var(--vkui--color_icon_primary, rgba(0, 0, 255, 0)) 0%', + 'var(--vkui--color_icon_primary, rgba(0, 0, 255, 0.05)) 15%', + 'var(--vkui--color_icon_primary, rgba(0, 0, 255, 0.2)) 30%', + 'var(--vkui--color_icon_primary, rgba(0, 0, 255, 0.8)) 70%', + 'var(--vkui--color_icon_primary, rgba(0, 0, 255, 0.95)) 85%', + 'var(--vkui--color_icon_primary, rgba(0, 0, 255, 1)) 100%', + ].join(', '), + ); + }); + + test('calculates gradient string from 2 colors', () => { const gradientToken = gradient([namedAlias('colorIconPrimary'), 'transparent']); const gradientValue = gradientToken({ colors: { colorIconPrimary: 'blue' } as any }); expect(gradientValue).toEqual( - 'var(--vkui--color_icon_primary, rgba(0, 0, 255, 1)) 0%, rgba(0, 0, 0, 0, 0) 100%', + 'var(--vkui--color_icon_primary, rgba(0, 0, 255, 1)) 0%, rgba(0, 0, 0, 0) 100%', ); }); - test('fails if bad number of arguments is given', () => { - expect(() => gradient([namedAlias('colorIconPrimary')])).toThrowError( - 'Gradient stops length (1) is not equal to the number of opacity points given (2)', + test('calculates gradient string from 3 colors', () => { + const gradientToken = gradient(['blue', 'black', 'red']); + const gradientValue = gradientToken({}); + + expect(gradientValue).toEqual( + 'rgba(0, 0, 255, 1) 0%, rgba(0, 0, 0, 1) 50%, rgba(255, 0, 0, 1) 100%', ); }); }); diff --git a/src/build/helpers/tokenHelpers.ts b/src/build/helpers/tokenHelpers.ts index f56cd61e..15f59d6b 100644 --- a/src/build/helpers/tokenHelpers.ts +++ b/src/build/helpers/tokenHelpers.ts @@ -3,7 +3,12 @@ import type { Property } from 'csstype'; import { ThemeDescription } from '@/interfaces/general'; import { Token } from '@/interfaces/general/tools/tokenValue'; -import { getGradientPointsFromColor, makeGradientPointRaw } from './getGradientPointsFromColor'; +import { + defaultOpacityPoints, + getGradientPointsFromColor, + makeGradientPointRaw, + OpacityPoints, +} from './getGradientPointsFromColor'; export type TokenFunction = (theme: Partial) => Token; export type NamedTokenFunction = ( @@ -32,25 +37,26 @@ export function staticRef(value: Token): T { return value; } +function makeOpacityPoints(count: number): OpacityPoints { + const result: OpacityPoints = []; + + for (let i = 0; i < count; i++) { + const percent = Math.round(i * (1 / (count - 1)) * 100); + result.push([1, percent]); + } + + return result; +} + export function gradient( stops: (Property.Color | NamedTokenFunction)[], - opacityPoints: OpacityPoint[] = [ - [1, 0], - [1, 100], - ], ): TokenFunction { - if (stops.length !== opacityPoints.length) { - throw new Error( - `Gradient stops length (${ - stops.length - }) is not equal to the number of opacity points given (${opacityPoints.length})`, - ); - } + const opacityPoints = stops.length > 1 ? makeOpacityPoints(stops.length) : defaultOpacityPoints; return (theme) => { return opacityPoints .map(([pointOpacity, pointCoordinate], index) => { - const stop = stops[index]; + const stop = stops[index] ?? stops[stops.length - 1]; const [stopKey, stopValue] = typeof stop === 'function' ? stop(theme) : [undefined, stop]; const pointRaw = makeGradientPointRaw(stopValue, stopKey);