import { SpecifyGradientValue, RawValueSignature, matchIsRawGradientConic, matchIsRawGradientLinear, matchIsRawGradientRadial, TokenState, } from '@specifyapp/specify-design-token-format'; import { colorValueToCss, rawColorToCss } from './color.js'; import { unwrapInnerValue } from '../../_utils/unwrapInnerValue.js'; import { DEFAULT_UNRESOLVABLE_STRATEGY } from '../aliasStrategies/throwOnUnresolvable.js'; import { CssResolvableAliasStrategy, CssUnresolvableAliasStrategy, } from '../aliasStrategies/CssAliasStrategy.js'; /** * Converts a `TokenState<'gradient'>` to a css value. * E.g: `linear-gradient(45deg, blue, red)` */ export function gradientToCss( aliasStrategy: CssResolvableAliasStrategy, unresolvableAliasStrategy: CssUnresolvableAliasStrategy = DEFAULT_UNRESOLVABLE_STRATEGY, ) { return (tokenState: TokenState<'gradient'>) => tokenState .getStatefulValueResult() .mapResolvableTopLevelAlias(aliasStrategy) .mapUnresolvableTopLevelAlias(alias => unresolvableAliasStrategy(tokenState, alias)) .mapTopLevelValue(value => { try { return value .mapRawValue(rawValue => { const output = rawGradientToCss(rawValue, aliasStrategy, unresolvableAliasStrategy); if (!output) throw new Error('prevent having undefined value'); return output; }) .mapResolvableModeLevelAlias(aliasStrategy) .mapUnresolvableModeLevelAlias(alias => unresolvableAliasStrategy(tokenState, alias)) .unwrap(); } catch (e) { return undefined; } }) .unwrap(); } /** * Converts a `RawValueSignature<'gradient'>` to css. */ export function rawGradientToCss( gradient: RawValueSignature<'gradient'>, aliasStrategy: CssResolvableAliasStrategy, unresolvableAliasStrategy: CssUnresolvableAliasStrategy = DEFAULT_UNRESOLVABLE_STRATEGY, ) { const colors = gradient.colorStops .resolveDeepValue() .mapPrimitiveValue(v => v .map(v => { const stop = v.resolveDeepValue().unwrapValue(); return `${unwrapInnerValue( stop.color.mapPrimitiveValue(color => rawColorToCss(color, aliasStrategy)), aliasStrategy, unresolvableAliasStrategy, )} ${stop.position.resolveDeepValue().unwrapValue() * 100}%`; }) .join(', '), ) .unwrapValue(); if (colors.length === 0) return undefined; if (matchIsRawGradientConic(gradient)) { return `conic-gradient(from ${gradient.angle .resolveDeepValue() .unwrapValue()}deg at ${gradient.position.resolveDeepValue().unwrapValue()}, ${colors})`; } else if (matchIsRawGradientLinear(gradient)) { return `linear-gradient(${gradient.angle.resolveDeepValue().unwrapValue()}deg, ${colors})`; } else if (matchIsRawGradientRadial(gradient)) { return `radial-gradient(circle at ${gradient.position .resolveDeepValue() .unwrapValue()}, ${colors})`; } else { throw new Error('Unreachable'); } } /** * Converts a gradient to css. */ export function gradientValueToCss(gradient: SpecifyGradientValue) { const colors = gradient.colorStops .map(({ color, position }) => `${colorValueToCss(color)} ${position * 100}%`) .join(', '); switch (gradient.type) { case 'conic': { return `conic-gradient(from ${gradient.angle}deg at ${gradient.position}, ${colors})`; } case 'radial': { return `radial-gradient(circle at ${gradient.position}, ${colors})`; } case 'linear': { return `linear-gradient(${gradient.angle}deg, ${colors})`; } } }