/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include #include #include #include #include #include namespace facebook::react { /** * One of the positional keywords for the `transform-origin` property. */ enum class CSSTransformOriginKeyword : std::underlying_type_t { Center = static_cast>(CSSKeyword::Center), Left = static_cast>(CSSKeyword::Left), Right = static_cast>(CSSKeyword::Right), Top = static_cast>(CSSKeyword::Top), Bottom = static_cast>(CSSKeyword::Bottom), }; static_assert(CSSDataType); /** * Representation of the value produced by the `transform-origin` property. * https://drafts.csswg.org/css-transforms/#propdef-transform-origin */ struct CSSTransformOrigin { std::variant x{}; std::variant y{}; CSSLength z{}; constexpr bool operator==(const CSSTransformOrigin& rhs) const = default; }; template <> struct CSSDataTypeParser { static constexpr auto consume(CSSSyntaxParser& parser) -> std::optional { // [ left | center | right | top | bottom | ] // | // [ left | center | right | ] // [ top | center | bottom | ] ? // | // [ [ center | left | right ] && [ center | top | bottom ] ] ? auto firstValue = parseNextCSSValue( parser); if (std::holds_alternative(firstValue)) { return {}; } auto secondValue = parseNextCSSValue( parser, CSSDelimiter::Whitespace); if (std::holds_alternative(secondValue)) { return singleValue(firstValue); } auto thirdValue = parseNextCSSValue(parser, CSSDelimiter::Whitespace); if (std::holds_alternative(firstValue) || std::holds_alternative(firstValue) || std::holds_alternative(secondValue) || std::holds_alternative(secondValue)) { return xyLengthPercentageValue(firstValue, secondValue, thirdValue); } if (std::holds_alternative(firstValue) && std::holds_alternative(secondValue)) { return xyKeywordValue( std::get(firstValue), std::get(secondValue), thirdValue); } return {}; } private: static constexpr CSSTransformOrigin singleValue( const std::variant< std::monostate, CSSLength, CSSPercentage, CSSTransformOriginKeyword>& value) { CSSTransformOrigin result{}; if (std::holds_alternative(value)) { result.x = std::get(value); result.y = keywordPercentage(CSSTransformOriginKeyword::Center); } else if (std::holds_alternative(value)) { result.x = std::get(value); result.y = keywordPercentage(CSSTransformOriginKeyword::Center); } else if (std::holds_alternative(value)) { if (isHorizontalKeyword(std::get(value))) { result.x = keywordPercentage(std::get(value)); result.y = keywordPercentage(CSSTransformOriginKeyword::Center); } else { result.x = keywordPercentage(CSSTransformOriginKeyword::Center); result.y = keywordPercentage(std::get(value)); } } return result; } static constexpr std::optional xyLengthPercentageValue( const std::variant< std::monostate, CSSLength, CSSPercentage, CSSTransformOriginKeyword>& val1, const std::variant< std::monostate, CSSLength, CSSPercentage, CSSTransformOriginKeyword>& val2, const std::variant& val3) { CSSTransformOrigin result{}; if (std::holds_alternative(val1)) { result.x = std::get(val1); } else if (std::holds_alternative(val1)) { result.x = std::get(val1); } else if (std::holds_alternative(val1)) { if (!isHorizontalKeyword(std::get(val1))) { return {}; } result.x = keywordPercentage(std::get(val1)); } if (std::holds_alternative(val2)) { result.y = std::get(val2); } else if (std::holds_alternative(val2)) { result.y = std::get(val2); } else if (std::holds_alternative(val2)) { if (!isVerticalKeyword(std::get(val2))) { return {}; } result.y = keywordPercentage(std::get(val2)); } if (std::holds_alternative(val3)) { result.z = std::get(val3); } return result; } static constexpr std::optional xyKeywordValue( CSSTransformOriginKeyword val1, CSSTransformOriginKeyword val2, const std::variant& val3) { if (isHorizontalKeyword(val1) && isVerticalKeyword(val2)) { return CSSTransformOrigin{ .x = keywordPercentage(val1), .y = keywordPercentage(val2), .z = std::holds_alternative(val3) ? std::get(val3) : CSSLength{}}; } if (isVerticalKeyword(val1) && isHorizontalKeyword(val2)) { return CSSTransformOrigin{ .x = keywordPercentage(val2), .y = keywordPercentage(val1), .z = std::holds_alternative(val3) ? std::get(val3) : CSSLength{}}; } return {}; } static constexpr bool isHorizontalKeyword(CSSTransformOriginKeyword keyword) { return keyword == CSSTransformOriginKeyword::Left || keyword == CSSTransformOriginKeyword::Center || keyword == CSSTransformOriginKeyword::Right; } static constexpr bool isVerticalKeyword(CSSTransformOriginKeyword keyword) { return keyword == CSSTransformOriginKeyword::Top || keyword == CSSTransformOriginKeyword::Center || keyword == CSSTransformOriginKeyword::Bottom; } static constexpr CSSPercentage keywordPercentage( CSSTransformOriginKeyword keyword) { switch (keyword) { case CSSTransformOriginKeyword::Left: return CSSPercentage{0.0f}; case CSSTransformOriginKeyword::Top: return CSSPercentage{0.0f}; case CSSTransformOriginKeyword::Center: return CSSPercentage{50.0f}; case CSSTransformOriginKeyword::Right: return CSSPercentage{100.0f}; case CSSTransformOriginKeyword::Bottom: return CSSPercentage{100.0f}; } return {}; } }; static_assert(CSSDataType); } // namespace facebook::react