import { SpecifyDimensionUnitValue, SpecifyDimensionValue, } from '@specifyapp/specify-design-token-format'; import { PX_TO_CM_RATIO, PX_TO_IN_RATIO, PX_TO_MM_RATIO, PX_TO_PC_RATIO, PX_TO_PT_RATIO, REM_DEFAULT_BASE, } from './constants.js'; import { BaseValue } from './updateToken.js'; // Rounding to 3 decimals export function roundNumber(v: number) { return Math.round((v + Number.EPSILON) * 1000) / 1000; } export function pxToCm(px: number) { return px * PX_TO_CM_RATIO; } export function pxToMm(px: number) { return px * PX_TO_MM_RATIO; } export function pxToIn(px: number) { return px * PX_TO_IN_RATIO; } export function pxToPt(px: number) { return px * PX_TO_PT_RATIO; } export function pxToPc(px: number) { return px * PX_TO_PC_RATIO; } export function pxToRem(px: number, baseValue: number = REM_DEFAULT_BASE) { return px / baseValue; } export function cmToPx(cm: number) { return cm / PX_TO_CM_RATIO; } export function mmToPx(mm: number) { return mm / PX_TO_MM_RATIO; } export function inToPx(in_: number) { return in_ / PX_TO_IN_RATIO; } export function ptToPx(pt: number) { return pt / PX_TO_PT_RATIO; } export function pcToPx(pc: number) { return pc / PX_TO_PC_RATIO; } export function remToPx(rem: number, baseValue: number = REM_DEFAULT_BASE) { return rem * baseValue; } type PartialRecord = { [P in K]?: T; }; // TIPS: If you want to support a new unit, I recommend to take the type below, set it to a new object with the Record type // so your IDE can generate all the cases for you, and then you pick what you're interested in. // Example: const test: Record<`${SpecifyDimensionUnitValue}:${SpecifyDimensionUnitValue}`, (value: number, baseValue: baseValue) => number> = {} export const converter: PartialRecord< `${SpecifyDimensionUnitValue}:${SpecifyDimensionUnitValue}`, (value: number, baseValue: BaseValue) => number > = { 'null:%': function (value: number, _baseValue: BaseValue) { return value; }, 'null:px': function (value: number, _baseValue: BaseValue) { return value; }, 'null:em': function (value: number, _baseValue: BaseValue) { return value; }, 'null:rem': function (value: number, _baseValue: BaseValue) { return value; }, 'null:pt': function (value: number, _baseValue: BaseValue) { return value; }, 'null:pc': function (value: number, _baseValue: BaseValue) { return value; }, 'null:in': function (value: number, _baseValue: BaseValue) { return value; }, 'null:cm': function (value: number, _baseValue: BaseValue) { return value; }, 'null:mm': function (value: number, _baseValue: BaseValue) { return value; }, 'null:ex': function (value: number, _baseValue: BaseValue) { return value; }, 'null:cap': function (value: number, _baseValue: BaseValue) { return value; }, 'null:ch': function (value: number, _baseValue: BaseValue) { return value; }, 'null:ic': function (value: number, _baseValue: BaseValue) { return value; }, 'null:lh': function (value: number, _baseValue: BaseValue) { return value; }, 'null:rlh': function (value: number, _baseValue: BaseValue) { return value; }, 'null:vw': function (value: number, _baseValue: BaseValue) { return value; }, 'null:svw': function (value: number, _baseValue: BaseValue) { return value; }, 'null:lvw': function (value: number, _baseValue: BaseValue) { return value; }, 'null:dvw': function (value: number, _baseValue: BaseValue) { return value; }, 'null:vh': function (value: number, _baseValue: BaseValue) { return value; }, 'null:svh': function (value: number, _baseValue: BaseValue) { return value; }, 'null:lvh': function (value: number, _baseValue: BaseValue) { return value; }, 'null:dvh': function (value: number, _baseValue: BaseValue) { return value; }, 'null:vi': function (value: number, _baseValue: BaseValue) { return value; }, 'null:svi': function (value: number, _baseValue: BaseValue) { return value; }, 'null:lvi': function (value: number, _baseValue: BaseValue) { return value; }, 'null:dvi': function (value: number, _baseValue: BaseValue) { return value; }, 'null:vb': function (value: number, _baseValue: BaseValue) { return value; }, 'null:svb': function (value: number, _baseValue: BaseValue) { return value; }, 'null:lvb': function (value: number, _baseValue: BaseValue) { return value; }, 'null:dvb': function (value: number, _baseValue: BaseValue) { return value; }, 'null:vmin': function (value: number, _baseValue: BaseValue) { return value; }, 'null:svmin': function (value: number, _baseValue: BaseValue) { return value; }, 'null:lvmin': function (value: number, _baseValue: BaseValue) { return value; }, 'null:dvmin': function (value: number, _baseValue: BaseValue) { return value; }, 'null:vmax': function (value: number, _baseValue: BaseValue) { return value; }, 'null:svmax': function (value: number, _baseValue: BaseValue) { return value; }, 'null:lvmax': function (value: number, _baseValue: BaseValue) { return value; }, 'null:dvmax': function (value: number, _baseValue: BaseValue) { return value; }, '%:vw': function (value: number, _baseValue: BaseValue) { return value; }, '%:svw': function (value: number, _baseValue: BaseValue) { return value; }, '%:lvw': function (value: number, _baseValue: BaseValue) { return value; }, '%:dvw': function (value: number, _baseValue: BaseValue) { return value; }, '%:vh': function (value: number, _baseValue: BaseValue) { return value; }, '%:svh': function (value: number, _baseValue: BaseValue) { return value; }, '%:lvh': function (value: number, _baseValue: BaseValue) { return value; }, '%:dvh': function (value: number, _baseValue: BaseValue) { return value; }, '%:vi': function (value: number, _baseValue: BaseValue) { return value; }, '%:svi': function (value: number, _baseValue: BaseValue) { return value; }, '%:lvi': function (value: number, _baseValue: BaseValue) { return value; }, '%:dvi': function (value: number, _baseValue: BaseValue) { return value; }, '%:vb': function (value: number, _baseValue: BaseValue) { return value; }, '%:svb': function (value: number, _baseValue: BaseValue) { return value; }, '%:lvb': function (value: number, _baseValue: BaseValue) { return value; }, '%:dvb': function (value: number, _baseValue: BaseValue) { return value; }, '%:vmin': function (value: number, _baseValue: BaseValue) { return value; }, '%:svmin': function (value: number, _baseValue: BaseValue) { return value; }, '%:lvmin': function (value: number, _baseValue: BaseValue) { return value; }, '%:dvmin': function (value: number, _baseValue: BaseValue) { return value; }, '%:vmax': function (value: number, _baseValue: BaseValue) { return value; }, '%:svmax': function (value: number, _baseValue: BaseValue) { return value; }, '%:lvmax': function (value: number, _baseValue: BaseValue) { return value; }, '%:dvmax': function (value: number, _baseValue: BaseValue) { return value; }, 'px:rem': function (value: number, baseValue: BaseValue) { return pxToRem(value, baseValue.rem); }, 'px:pt': function (value: number, _baseValue: BaseValue) { return pxToPt(value); }, 'px:pc': function (value: number, _baseValue: BaseValue) { return pxToPc(value); }, 'px:in': function (value: number, _baseValue: BaseValue) { return pxToIn(value); }, 'px:cm': function (value: number, _baseValue: BaseValue) { return pxToCm(value); }, 'px:mm': function (value: number, _baseValue: BaseValue) { return pxToMm(value); }, 'rem:px': function (value: number, baseValue: BaseValue) { return remToPx(value, baseValue.rem); }, 'rem:pt': function (value: number, baseValue: BaseValue) { return pxToPt(remToPx(value, baseValue.rem)); }, 'rem:pc': function (value: number, baseValue: BaseValue) { return pxToPc(remToPx(value, baseValue.rem)); }, 'rem:in': function (value: number, baseValue: BaseValue) { return pxToIn(remToPx(value, baseValue.rem)); }, 'rem:cm': function (value: number, baseValue: BaseValue) { return pxToCm(remToPx(value, baseValue.rem)); }, 'rem:mm': function (value: number, baseValue: BaseValue) { return pxToMm(remToPx(value, baseValue.rem)); }, 'pt:px': function (value: number, _baseValue: BaseValue) { return ptToPx(value); }, 'pt:rem': function (value: number, baseValue: BaseValue) { return pxToRem(ptToPx(value), baseValue.rem); }, 'pt:pc': function (value: number, _baseValue: BaseValue) { return pxToPc(ptToPx(value)); }, 'pt:in': function (value: number, _baseValue: BaseValue) { return pxToIn(ptToPx(value)); }, 'pt:cm': function (value: number, _baseValue: BaseValue) { return pxToCm(ptToPx(value)); }, 'pt:mm': function (value: number, _baseValue: BaseValue) { return pxToMm(ptToPx(value)); }, 'pc:px': function (value: number, _baseValue: BaseValue) { return pcToPx(value); }, 'pc:rem': function (value: number, baseValue: BaseValue) { return pxToRem(pcToPx(value), baseValue.rem); }, 'pc:pt': function (value: number, _baseValue: BaseValue) { return pxToPt(ptToPx(value)); }, 'pc:in': function (value: number, _baseValue: BaseValue) { return pxToIn(ptToPx(value)); }, 'pc:cm': function (value: number, _baseValue: BaseValue) { return pxToCm(ptToPx(value)); }, 'pc:mm': function (value: number, _baseValue: BaseValue) { return pxToMm(ptToPx(value)); }, 'in:px': function (value: number, _baseValue: BaseValue) { return inToPx(value); }, 'in:rem': function (value: number, baseValue: BaseValue) { return pxToRem(inToPx(value), baseValue.rem); }, 'in:pt': function (value: number, _baseValue: BaseValue) { return pxToPt(inToPx(value)); }, 'in:pc': function (value: number, _baseValue: BaseValue) { return pxToPc(inToPx(value)); }, 'in:cm': function (value: number, _baseValue: BaseValue) { return pxToCm(inToPx(value)); }, 'in:mm': function (value: number, _baseValue: BaseValue) { return pxToMm(inToPx(value)); }, 'cm:px': function (value: number, _baseValue: BaseValue) { return cmToPx(value); }, 'cm:rem': function (value: number, baseValue: BaseValue) { return pxToRem(cmToPx(value), baseValue.rem); }, 'cm:pt': function (value: number, _baseValue: BaseValue) { return pxToPt(cmToPx(value)); }, 'cm:pc': function (value: number, _baseValue: BaseValue) { return pxToPc(cmToPx(value)); }, 'cm:in': function (value: number, _baseValue: BaseValue) { return pxToIn(cmToPx(value)); }, 'cm:mm': function (value: number, _baseValue: BaseValue) { return pxToMm(cmToPx(value)); }, 'mm:px': function (value: number, _baseValue: BaseValue) { return mmToPx(value); }, 'mm:rem': function (value: number, baseValue: BaseValue) { return pxToRem(mmToPx(value), baseValue.rem); }, 'mm:pt': function (value: number, _baseValue: BaseValue) { return pxToPt(mmToPx(value)); }, 'mm:pc': function (value: number, _baseValue: BaseValue) { return pxToPc(mmToPx(value)); }, 'mm:in': function (value: number, _baseValue: BaseValue) { return pxToIn(mmToPx(value)); }, 'mm:cm': function (value: number, _baseValue: BaseValue) { return pxToCm(mmToPx(value)); }, 'vw:%': function (value: number, _baseValue: BaseValue) { return value; }, 'svw:%': function (value: number, _baseValue: BaseValue) { return value; }, 'lvw:%': function (value: number, _baseValue: BaseValue) { return value; }, 'dvw:%': function (value: number, _baseValue: BaseValue) { return value; }, 'vh:%': function (value: number, _baseValue: BaseValue) { return value; }, 'svh:%': function (value: number, _baseValue: BaseValue) { return value; }, 'lvh:%': function (value: number, _baseValue: BaseValue) { return value; }, 'dvh:%': function (value: number, _baseValue: BaseValue) { return value; }, 'vi:%': function (value: number, _baseValue: BaseValue) { return value; }, 'svi:%': function (value: number, _baseValue: BaseValue) { return value; }, 'lvi:%': function (value: number, _baseValue: BaseValue) { return value; }, 'dvi:%': function (value: number, _baseValue: BaseValue) { return value; }, 'vb:%': function (value: number, _baseValue: BaseValue) { return value; }, 'svb:%': function (value: number, _baseValue: BaseValue) { return value; }, 'lvb:%': function (value: number, _baseValue: BaseValue) { return value; }, 'dvb:%': function (value: number, _baseValue: BaseValue) { return value; }, 'vmin:%': function (value: number, _baseValue: BaseValue) { return value; }, 'svmin:%': function (value: number, _baseValue: BaseValue) { return value; }, 'lvmin:%': function (value: number, _baseValue: BaseValue) { return value; }, 'dvmin:%': function (value: number, _baseValue: BaseValue) { return value; }, 'vmax:%': function (value: number, _baseValue: BaseValue) { return value; }, 'svmax:%': function (value: number, _baseValue: BaseValue) { return value; }, 'lvmax:%': function (value: number, _baseValue: BaseValue) { return value; }, 'dvmax:%': function (value: number, _baseValue: BaseValue) { return value; }, }; export function convertDimension( { value, unit }: SpecifyDimensionValue, targetUnit: SpecifyDimensionUnitValue, baseValue: BaseValue, ): SpecifyDimensionValue | undefined { if (targetUnit === null) { return { unit: null, value }; } else if (unit === null || unit === targetUnit) { return { unit: targetUnit, value }; } const output = converter[`${unit}:${targetUnit}`]?.(value, baseValue); if (!output) return undefined; return { value: roundNumber(output), unit: targetUnit }; }