UNPKG

@wix/css-property-parser

Version:

A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance

170 lines (169 loc) 5.53 kB
"use strict"; // CSS gap property parser - Phase 3 refactored // Handles parsing of CSS gap property according to MDN specification // https://developer.mozilla.org/en-US/docs/Web/CSS/gap Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = parse; exports.toCSSValue = toCSSValue; const length_1 = require('./length.cjs'); const percentage_1 = require('./percentage.cjs'); const css_variable_1 = require('./css-variable.cjs'); const shared_utils_1 = require('../utils/shared-utils.cjs'); // ======================================== // Utilities // ======================================== /** * Checks if a string is a valid gap value component */ function isGapValue(value) { const trimmed = value.trim(); // CSS variables are valid gap values if ((0, shared_utils_1.isCssVariable)(trimmed)) { return true; } // Global keywords are valid if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) { return true; } // Normal keyword is valid for gap if (trimmed.toLowerCase() === 'normal') { return true; } // Try to parse as percentage (must be non-negative) const percentageResult = (0, percentage_1.parse)(trimmed); if (percentageResult) { return (0, shared_utils_1.isNonNegative)(percentageResult); } // Try to parse as length (must be non-negative) const lengthResult = (0, length_1.parse)(trimmed); if (lengthResult) { return (0, shared_utils_1.isNonNegative)(lengthResult); } return false; } /** * Parses a single gap value into atomic type */ function parseGapValue(value) { const trimmed = value.trim(); // Handle CSS variables in individual components if ((0, shared_utils_1.isCssVariable)(trimmed)) { return (0, css_variable_1.parse)(trimmed); } // First validate the value if (!isGapValue(trimmed)) { return null; } // Handle keywords if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) { return { type: 'keyword', keyword: trimmed.toLowerCase() }; } if (trimmed.toLowerCase() === 'normal') { return { type: 'keyword', keyword: 'normal' }; } // Try percentage first const percentageResult = (0, percentage_1.parse)(trimmed); if (percentageResult) { return percentageResult; } // Try length const lengthResult = (0, length_1.parse)(trimmed); if (lengthResult) { return lengthResult; } return null; } // ======================================== // Main Functions // ======================================== /** * Parses a CSS gap property value into structured components * @param value - The CSS gap property string * @returns Parsed gap object with rowGap and columnGap or null if invalid */ function parse(value) { if (!value || typeof value !== 'string') { return null; } const trimmed = value.trim(); if (trimmed === '') { return null; } // CSS variables can be parsed directly for entire shorthand if ((0, shared_utils_1.isCssVariable)(trimmed)) { return (0, css_variable_1.parse)(trimmed); } if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) { const keyword = { type: 'keyword', keyword: trimmed.toLowerCase() }; return { rowGap: keyword, columnGap: keyword }; } // Use shared tokenize utility to handle calc expressions properly const values = (0, shared_utils_1.tokenize)(trimmed); if (values.length === 0 || values.length > 2) { return null; } const parsedValues = []; for (const val of values) { const parsedValue = parseGapValue(val); if (!parsedValue) { return null; } parsedValues.push(parsedValue); } if (parsedValues.length === 1) { return { rowGap: parsedValues[0], columnGap: parsedValues[0] }; } else { return { rowGap: parsedValues[0], columnGap: parsedValues[1] }; } } /** * Converts a parsed gap back to a CSS value string * @param parsed - The parsed gap object * @returns CSS value string or null if invalid */ function toCSSValue(parsed) { if (!parsed) { return null; } // Handle CSS variables for entire shorthand if ('CSSvariable' in parsed) { return (0, css_variable_1.toCSSValue)(parsed); } // Handle expanded gap (GapExpanded) const gapExpanded = parsed; const rowGapValue = convertGapValueToCSSValue(gapExpanded.rowGap); const columnGapValue = convertGapValueToCSSValue(gapExpanded.columnGap); if (!rowGapValue || !columnGapValue) { return null; } // If both values are the same, return single value if (rowGapValue === columnGapValue) { return rowGapValue; } // Return two values return `${rowGapValue} ${columnGapValue}`; } /** * Converts a single gap value back to CSS string */ function convertGapValueToCSSValue(gapValue) { // Handle CSS variables in components if ('CSSvariable' in gapValue) { return (0, css_variable_1.toCSSValue)(gapValue); } // Handle keywords if ('keyword' in gapValue) { return gapValue.keyword; } // Handle length/percentage by delegating to the appropriate toCSSValue function const lengthValue = (0, length_1.toCSSValue)(gapValue); if (lengthValue) { return lengthValue; } const percentageValue = (0, percentage_1.toCSSValue)(gapValue); if (percentageValue) { return percentageValue; } return null; }