@wix/css-property-parser
Version:
A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance
208 lines (207 loc) • 7.29 kB
JavaScript
"use strict";
// Opacity property parser
// Handles parsing of CSS opacity property according to MDN specification
// https://developer.mozilla.org/en-US/docs/Web/CSS/opacity
Object.defineProperty(exports, "__esModule", { value: true });
exports.parse = parse;
exports.toCSSValue = toCSSValue;
const shared_utils_1 = require('../utils/shared-utils.cjs');
const number_1 = require('./number.cjs');
const percentage_1 = require('./percentage.cjs');
const css_variable_1 = require('./css-variable.cjs');
/**
* Check if a number value is in valid opacity range (0-1)
*/
function isValidOpacityNumber(numberValue) {
// CSS variables can't be validated at parse time
if ('CSSvariable' in numberValue) {
return true;
}
if ('expression' in numberValue) {
// Allow calc expressions (can't validate at parse time)
return true;
}
if ('keyword' in numberValue) {
// Allow keywords (inherit, initial, etc.)
return true;
}
// Check range for numeric values
return numberValue.value >= 0 && numberValue.value <= 1;
}
/**
* Check if a percentage value is in valid opacity range (0%-100%)
*/
function isValidOpacityPercentage(percentageValue) {
// CSS variables can't be validated at parse time
if ('CSSvariable' in percentageValue) {
return true;
}
if ('expression' in percentageValue) {
// Allow calc expressions (can't validate at parse time)
return true;
}
// Check range for percentage values
return percentageValue.value >= 0 && percentageValue.value <= 100;
}
/**
* Parses a CSS opacity property string into structured components
* Follows MDN specification: https://developer.mozilla.org/en-US/docs/Web/CSS/opacity
*/
function parse(value) {
if (!value || typeof value !== 'string') {
return null;
}
const trimmed = value.trim();
if (trimmed === '') {
return null;
}
// CSS variables can be parsed directly
if ((0, shared_utils_1.isCssVariable)(trimmed)) {
return (0, css_variable_1.parse)(trimmed);
}
// Handle global keywords (inherit, initial, unset, revert, revert-layer)
if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) {
return { type: 'keyword', keyword: trimmed.toLowerCase() };
}
// Handle calc expressions by checking content
if (trimmed.startsWith('calc(') && trimmed.endsWith(')')) {
const expression = trimmed.slice(5, -1).trim();
// If calc expression contains %, treat as percentage
if (expression.includes('%')) {
const percentageResult = (0, percentage_1.parse)(trimmed);
if (percentageResult && isValidOpacityPercentage(percentageResult)) {
// Handle CSS variables
if ('CSSvariable' in percentageResult) {
return percentageResult;
}
// Return calc expression for percentage
if ('expression' in percentageResult) {
return {
type: 'percentage',
value: 0, // placeholder - actual value computed at runtime
unit: '%'
};
}
else {
return {
type: 'percentage',
value: percentageResult.value,
unit: '%'
};
}
}
}
else {
// Otherwise treat as number
const numberResult = (0, number_1.parse)(trimmed);
if (numberResult && isValidOpacityNumber(numberResult)) {
// Handle CSS variables
if ('CSSvariable' in numberResult) {
return numberResult;
}
// Return calc expression for number
if ('expression' in numberResult) {
return {
type: 'number',
value: 0 // placeholder - actual value computed at runtime
};
}
else if ('keyword' in numberResult) {
// Handle keyword case
return { type: 'keyword', keyword: numberResult.keyword };
}
else {
return {
type: 'number',
value: numberResult.value
};
}
}
}
return null;
}
// Try to parse as percentage first (for explicit percentage values like "50%")
if (trimmed.includes('%')) {
const percentageResult = (0, percentage_1.parse)(trimmed);
if (percentageResult) {
// Validate percentage range (0%-100%)
if (isValidOpacityPercentage(percentageResult)) {
// Handle CSS variables
if ('CSSvariable' in percentageResult) {
return percentageResult;
}
if ('expression' in percentageResult) {
return {
type: 'percentage',
value: 0, // placeholder - actual value computed at runtime
unit: '%'
};
}
else {
return {
type: 'percentage',
value: percentageResult.value,
unit: '%'
};
}
}
// Invalid range - return null
return null;
}
}
// Try to parse as number
const numberResult = (0, number_1.parse)(trimmed);
if (numberResult) {
// Validate number range (0-1)
if (isValidOpacityNumber(numberResult)) {
// Handle CSS variables
if ('CSSvariable' in numberResult) {
return numberResult;
}
if ('expression' in numberResult) {
return {
type: 'number',
value: 0 // placeholder - actual value computed at runtime
};
}
else if ('keyword' in numberResult) {
// Handle keyword case
return { type: 'keyword', keyword: numberResult.keyword };
}
else {
return {
type: 'number',
value: numberResult.value
};
}
}
// Invalid range - return null
return null;
}
return null;
}
/**
* Converts a parsed opacity back to a CSS value string
*/
function toCSSValue(parsed) {
if (!parsed) {
return null;
}
// Handle CSS variables
if ('CSSvariable' in parsed) {
return (0, css_variable_1.toCSSValue)(parsed);
}
// Handle keywords
if ('keyword' in parsed) {
return parsed.keyword;
}
// Handle percentage values (has unit property)
if ('unit' in parsed && parsed.unit === '%') {
return `${parsed.value}%`;
}
// Handle number values (just value property)
if ('value' in parsed && !('unit' in parsed)) {
return parsed.value.toString();
}
return null;
}