export type Unit = 'px' | 'em' | '%' | 'rem' | 'ratio' | 'fr' export type Length = { unit: Unit; value: number } export function normalizeStringLength(length: string): Length { let parsedUnit = length.match(/px|em|%|rem|ratio|fr/) let parsedValue = parseFloat(length) const parsedRatio = length?.match(/\s*([\d+.]+)\s*:\s*([\d+.]+)\s*/) if (parsedRatio) { const ratio = parseFloat(parsedRatio[1]) / parseFloat(parsedRatio[2]) if (!Number.isNaN(ratio)) { return { value: ratio, unit: 'ratio' } } } let unit: Unit = 'px' let value: number = 0 if (parsedUnit) { unit = parsedUnit[0] as Unit } if (!Number.isNaN(parsedValue)) { value = parsedValue } return { value, unit } } export function resolveLength(length: Length, base: number) { if (length.unit == '%') return (length.value / 100) * base return length.value } export function stringifyLength(length: Length | number | string, defaultValue = '0px') { if (length && typeof length == 'object') { // Skip NaNs if (parseInt(String(length.value)) !== parseInt(String(length.value))) { return defaultValue } if (length.unit?.match(/%|ratio|em|px|em|rem|fr|vmax|vmin|vw|vh/)) return length.value + length.unit return length.value + 'px' } if (typeof length == 'string' && length.match(/%|ratio|em|px|em|rem|fr|vmax|vmin|vw|vh/)) { return length } if (length == null) { return defaultValue } return length + 'px' } export const Length = (length: any): Length => { if (typeof length === 'string') { return normalizeStringLength(length) } if (typeof length === 'number') { return { value: normalizeStringLength(`${length}px`).value, unit: 'px' } } if (length && (length.value || length.unit)) { return normalizeStringLength(`${length.value}${length.unit}`) } return { value: 0, unit: 'px' } } export const NullLength = (length: any): null => { if (length === '' || length == null || length?.value === null) { return null } } export const pickFirst = (object: T, value: any): T => { if (object === undefined && value !== undefined) { return value } return object } export const pickNonEmpty = (object: T, value: any): T => { if (!(object instanceof Array) || object.length == 0) { return value } return object } export function LengthOrPosition(value: any) { switch (value) { case 'left': case 'top': var nextValue = '0%' break case 'right': case 'bottom': var nextValue = '100%' break case null: case undefined: case 'center': var nextValue = '50%' } return Length(nextValue ?? value) }