import type { CalcFor_DimensionPercentageFor_LengthValue, CalcFor_Length, CssColor, Function as FunctionType } from 'lightningcss' import { Logger } from '../logger' import type { ProcessorBuilder } from './processor' import { pipe } from './utils' export class Functions { private readonly logger = new Logger('Functions') constructor(private readonly Processor: ProcessorBuilder) {} processCalc(calc: CalcFor_DimensionPercentageFor_LengthValue | CalcFor_Length): string { switch (calc.type) { case 'sum': { const sum = calc.value.map(x => this.processCalc(x)).join(' + ') return this.tryEval(sum) } case 'value': return this.Processor.CSS.processValue(calc.value) case 'function': return this.Processor.CSS.processValue(calc.value) case 'number': return String(calc.value) default: this.logger.warn(`Unsupported calc type - ${calc.type}`) return '' } } processFunction(fn: string | FunctionType) { if (typeof fn !== 'object') { this.logger.warn(`Unsupported function - ${fn}`) return fn } if (fn.name === 'calc') { const calc = this.Processor.CSS.processValue(fn.arguments) return pipe(calc)( String, x => this.Processor.Functions.tryEval(x), ) } if (fn.name === 'cubic-bezier') { const cubicArguments = pipe(this.Processor.CSS.processValue(fn.arguments))( String, x => x.replace(/,\s/g, ','), ) return `rt.cubicBezier(${cubicArguments})` } if (fn.name === 'max') { return `Math.max(${this.Processor.CSS.processValue(fn.arguments)})` } if (fn.name === 'linear-gradient') { return this.Processor.CSS.processValue(fn.arguments) } if (fn.name === 'color-mix') { return this.processColorMix(fn) } if ( [ 'rgb', 'oklab', 'oklch', 'hsl', 'hwb', 'lab', 'lch', 'srgb', ].includes(fn.name) ) { const color = `${fn.name}(${this.Processor.CSS.processValue(fn.arguments)})` return this.Processor.Color.processColor(color as CssColor) } if ( [ 'blur', 'brightness', 'contrast', 'grayscale', 'hue-rotate', 'invert', 'opacity', 'saturate', 'sepia', 'conic-gradient', 'radial-gradient', ].includes(fn.name) ) { // Not supported by RN return '""' } if (['skewX', 'skewY'].includes(fn.name)) { return `"${fn.name}(${this.Processor.CSS.processValue(fn.arguments).replace(/"/g, '')})"` } if (fn.name === 'hairlineWidth') { return 'rt.hairlineWidth' } if (fn.name === 'pixelRatio') { if (fn.arguments.length === 0) { return 'rt.pixelRatio(1)' } return `rt.pixelRatio(${this.Processor.CSS.processValue(fn.arguments)})` } if (fn.name === 'fontScale') { if (fn.arguments.length === 0) { return 'rt.fontScale(1)' } return `rt.fontScale(${this.Processor.CSS.processValue(fn.arguments)})` } if (fn.name === 'drop-shadow') { return undefined } this.logger.warn(`Unsupported function - ${fn.name}`) return fn.name } processMathFunction( name: string, value: | Array | Array | CalcFor_DimensionPercentageFor_LengthValue | CalcFor_Length, ) { if (!Array.isArray(value)) { return `Math.${name}(${this.processCalc(value)})` } const values = value.map(x => this.processCalc(x)).join(' , ') return `Math.${name}(${values})` } private tryEval(value: string) { // Match units like %, deg, rad, grad, turn that are not preceded by letters or hyphens const units = Array.from( value .replace(/"/g, '') .match(/(? pipe(arg)( x => this.Processor.CSS.processValue(x), String, x => x.trim(), ) ) .filter(token => !['', ',', 'in', 'srgb', 'rgb', 'hsl', 'hwb', 'lab', 'lch', 'oklab', 'oklch'].includes(token.replace(/"/g, ''))) return `rt.colorMix(${tokens.join(', ')})` } }