mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Reduce transform parsing duplication (#49279)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49279 I ended up using this same pattern for filter parsing where the logic betweeen functions is very similar. Let's deduplicate the logic for transform parsing a bit. This also separates `rotate()` and `rotateZ()` types, to be handled the same at a different layer. Changelog: [Internal] Reviewed By: joevilches Differential Revision: D69326443 fbshipit-source-id: 9bf910c6d4e07748ff032433167576f9d58cd8d6
This commit is contained in:
committed by
Facebook GitHub Bot
parent
722f5ba786
commit
f40d69f06d
@@ -8,8 +8,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include <react/renderer/css/CSSAngle.h>
|
||||
#include <react/renderer/css/CSSCompoundDataType.h>
|
||||
@@ -20,10 +20,90 @@
|
||||
#include <react/renderer/css/CSSNumber.h>
|
||||
#include <react/renderer/css/CSSValueParser.h>
|
||||
#include <react/renderer/css/CSSZero.h>
|
||||
#include <react/utils/TemplateStringLiteral.h>
|
||||
#include <react/utils/iequals.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
namespace detail {
|
||||
template <
|
||||
typename DataT,
|
||||
TemplateStringLiteral Name,
|
||||
CSSDataType... AllowedComponentsT>
|
||||
requires(std::is_same_v<
|
||||
decltype(DataT::value),
|
||||
std::variant<AllowedComponentsT...>>)
|
||||
struct CSSVariantComponentTransformParser {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<DataT> {
|
||||
if (!iequals(func.name, Name)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto val = parseNextCSSValue<AllowedComponentsT...>(parser);
|
||||
|
||||
return std::visit(
|
||||
[&](auto&& v) -> std::optional<DataT> {
|
||||
if constexpr (std::is_same_v<
|
||||
std::remove_cvref_t<decltype(v)>,
|
||||
std::monostate>) {
|
||||
return {};
|
||||
} else {
|
||||
return DataT{.value = std::forward<decltype(v)>(v)};
|
||||
}
|
||||
},
|
||||
val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DataT, TemplateStringLiteral Name>
|
||||
requires(std::is_same_v<decltype(DataT::value), float>)
|
||||
struct CSSNumberPercentTransformParser {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<DataT> {
|
||||
if (!iequals(func.name, Name)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto val = parseNextCSSValue<CSSNumber, CSSPercentage>(parser);
|
||||
if (std::holds_alternative<std::monostate>(val)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return DataT{
|
||||
.value = std::holds_alternative<CSSNumber>(val)
|
||||
? std::get<CSSNumber>(val).value
|
||||
: std::get<CSSPercentage>(val).value / 100.0f};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DataT, TemplateStringLiteral Name>
|
||||
requires(std::is_same_v<decltype(DataT::degrees), float>)
|
||||
struct CSSAngleTransformParser {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<DataT> {
|
||||
if (!iequals(func.name, Name)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto value = parseNextCSSValue<CSSAngle, CSSZero>(parser);
|
||||
if (std::holds_alternative<std::monostate>(value)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return DataT{
|
||||
.degrees = std::holds_alternative<CSSAngle>(value)
|
||||
? std::get<CSSAngle>(value).degrees
|
||||
: 0.0f,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Representation of matrix() transform function.
|
||||
*/
|
||||
@@ -164,26 +244,12 @@ struct CSSTranslateX {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSTranslateX> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSTranslateX> {
|
||||
if (!iequals(func.name, "translateX")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto val = parseNextCSSValue<CSSLengthPercentage>(parser);
|
||||
if (std::holds_alternative<std::monostate>(val)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSTranslateX{
|
||||
.value = std::holds_alternative<CSSLength>(val)
|
||||
? std::variant<CSSLength, CSSPercentage>{std::get<CSSLength>(val)}
|
||||
: std::variant<CSSLength, CSSPercentage>{
|
||||
std::get<CSSPercentage>(val)}};
|
||||
}
|
||||
};
|
||||
struct CSSDataTypeParser<CSSTranslateX>
|
||||
: public detail::CSSVariantComponentTransformParser<
|
||||
CSSTranslateX,
|
||||
"translateX",
|
||||
CSSLength,
|
||||
CSSPercentage> {};
|
||||
|
||||
static_assert(CSSDataType<CSSTranslateX>);
|
||||
|
||||
@@ -197,26 +263,12 @@ struct CSSTranslateY {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSTranslateY> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSTranslateY> {
|
||||
if (!iequals(func.name, "translateY")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto val = parseNextCSSValue<CSSLengthPercentage>(parser);
|
||||
if (std::holds_alternative<std::monostate>(val)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSTranslateY{
|
||||
.value = std::holds_alternative<CSSLength>(val)
|
||||
? std::variant<CSSLength, CSSPercentage>{std::get<CSSLength>(val)}
|
||||
: std::variant<CSSLength, CSSPercentage>{
|
||||
std::get<CSSPercentage>(val)}};
|
||||
}
|
||||
};
|
||||
struct CSSDataTypeParser<CSSTranslateY>
|
||||
: public detail::CSSVariantComponentTransformParser<
|
||||
CSSTranslateY,
|
||||
"translateY",
|
||||
CSSLength,
|
||||
CSSPercentage> {};
|
||||
|
||||
static_assert(CSSDataType<CSSTranslateY>);
|
||||
|
||||
@@ -274,25 +326,8 @@ struct CSSScaleX {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSScaleX> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSScaleX> {
|
||||
if (!iequals(func.name, "scaleX")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto x = parseNextCSSValue<CSSNumber, CSSPercentage>(parser);
|
||||
if (std::holds_alternative<std::monostate>(x)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSScaleX{
|
||||
.value = std::holds_alternative<CSSNumber>(x)
|
||||
? std::get<CSSNumber>(x).value
|
||||
: std::get<CSSPercentage>(x).value / 100.0f};
|
||||
}
|
||||
};
|
||||
struct CSSDataTypeParser<CSSScaleX>
|
||||
: public detail::CSSNumberPercentTransformParser<CSSScaleX, "scaleX"> {};
|
||||
|
||||
static_assert(CSSDataType<CSSScaleX>);
|
||||
|
||||
@@ -306,61 +341,25 @@ struct CSSScaleY {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSScaleY> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSScaleY> {
|
||||
if (!iequals(func.name, "scaleY")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto y = parseNextCSSValue<CSSNumber, CSSPercentage>(parser);
|
||||
if (std::holds_alternative<std::monostate>(y)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSScaleY{
|
||||
.value = std::holds_alternative<CSSNumber>(y)
|
||||
? std::get<CSSNumber>(y).value
|
||||
: std::get<CSSPercentage>(y).value / 100.0f};
|
||||
}
|
||||
};
|
||||
struct CSSDataTypeParser<CSSScaleY>
|
||||
: public detail::CSSNumberPercentTransformParser<CSSScaleY, "scaleY"> {};
|
||||
|
||||
static_assert(CSSDataType<CSSScaleY>);
|
||||
|
||||
/**
|
||||
* Representation of rotate() or rotateZ() transform function.
|
||||
* Representation of rotate() transform function.
|
||||
*/
|
||||
struct CSSRotateZ {
|
||||
struct CSSRotate {
|
||||
float degrees{};
|
||||
|
||||
constexpr bool operator==(const CSSRotateZ& rhs) const = default;
|
||||
constexpr bool operator==(const CSSRotate& rhs) const = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSRotateZ> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSRotateZ> {
|
||||
if (!(iequals(func.name, "rotate") || iequals(func.name, "rotateZ"))) {
|
||||
return {};
|
||||
}
|
||||
struct CSSDataTypeParser<CSSRotate>
|
||||
: public detail::CSSAngleTransformParser<CSSRotate, "rotate"> {};
|
||||
|
||||
auto value = parseNextCSSValue<CSSAngle, CSSZero>(parser);
|
||||
if (std::holds_alternative<std::monostate>(value)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSRotateZ{
|
||||
.degrees = std::holds_alternative<CSSAngle>(value)
|
||||
? std::get<CSSAngle>(value).degrees
|
||||
: 0.0f,
|
||||
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(CSSDataType<CSSRotateZ>);
|
||||
static_assert(CSSDataType<CSSRotate>);
|
||||
|
||||
/**
|
||||
* Representation of rotateX() transform function.
|
||||
@@ -372,26 +371,8 @@ struct CSSRotateX {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSRotateX> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSRotateX> {
|
||||
if (!iequals(func.name, "rotateX")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto value = parseNextCSSValue<CSSAngle, CSSZero>(parser);
|
||||
if (std::holds_alternative<std::monostate>(value)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSRotateX{
|
||||
.degrees = std::holds_alternative<CSSAngle>(value)
|
||||
? std::get<CSSAngle>(value).degrees
|
||||
: 0.0f,
|
||||
};
|
||||
}
|
||||
};
|
||||
struct CSSDataTypeParser<CSSRotateX>
|
||||
: public detail::CSSAngleTransformParser<CSSRotateX, "rotateX"> {};
|
||||
|
||||
static_assert(CSSDataType<CSSRotateX>);
|
||||
|
||||
@@ -405,29 +386,26 @@ struct CSSRotateY {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSRotateY> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSRotateY> {
|
||||
if (!iequals(func.name, "rotateY")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto value = parseNextCSSValue<CSSAngle, CSSZero>(parser);
|
||||
if (std::holds_alternative<std::monostate>(value)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSRotateY{
|
||||
.degrees = std::holds_alternative<CSSAngle>(value)
|
||||
? std::get<CSSAngle>(value).degrees
|
||||
: 0.0f,
|
||||
};
|
||||
}
|
||||
};
|
||||
struct CSSDataTypeParser<CSSRotateY>
|
||||
: public detail::CSSAngleTransformParser<CSSRotateY, "rotateY"> {};
|
||||
|
||||
static_assert(CSSDataType<CSSRotateY>);
|
||||
|
||||
/**
|
||||
* Representation of rotateZ() transform function.
|
||||
*/
|
||||
struct CSSRotateZ {
|
||||
float degrees{};
|
||||
|
||||
constexpr bool operator==(const CSSRotateZ& rhs) const = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSRotateZ>
|
||||
: public detail::CSSAngleTransformParser<CSSRotateZ, "rotateZ"> {};
|
||||
|
||||
static_assert(CSSDataType<CSSRotateZ>);
|
||||
|
||||
/**
|
||||
* Representation of skewX() transform function.
|
||||
*/
|
||||
@@ -438,26 +416,8 @@ struct CSSSkewX {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSSkewX> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSSkewX> {
|
||||
if (!iequals(func.name, "skewX")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto value = parseNextCSSValue<CSSAngle, CSSZero>(parser);
|
||||
if (std::holds_alternative<std::monostate>(value)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSSkewX{
|
||||
.degrees = std::holds_alternative<CSSAngle>(value)
|
||||
? std::get<CSSAngle>(value).degrees
|
||||
: 0.0f,
|
||||
};
|
||||
}
|
||||
};
|
||||
struct CSSDataTypeParser<CSSSkewX>
|
||||
: public detail::CSSAngleTransformParser<CSSSkewX, "skewX"> {};
|
||||
|
||||
static_assert(CSSDataType<CSSSkewX>);
|
||||
|
||||
@@ -471,26 +431,8 @@ struct CSSSkewY {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CSSDataTypeParser<CSSSkewY> {
|
||||
static constexpr auto consumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& parser) -> std::optional<CSSSkewY> {
|
||||
if (!iequals(func.name, "skewY")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto value = parseNextCSSValue<CSSAngle, CSSZero>(parser);
|
||||
if (std::holds_alternative<std::monostate>(value)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSSkewY{
|
||||
.degrees = std::holds_alternative<CSSAngle>(value)
|
||||
? std::get<CSSAngle>(value).degrees
|
||||
: 0.0f,
|
||||
};
|
||||
}
|
||||
};
|
||||
struct CSSDataTypeParser<CSSSkewY>
|
||||
: public detail::CSSAngleTransformParser<CSSSkewY, "skewY"> {};
|
||||
|
||||
static_assert(CSSDataType<CSSSkewY>);
|
||||
|
||||
@@ -539,9 +481,10 @@ using CSSTransformFunction = CSSCompoundDataType<
|
||||
CSSScale,
|
||||
CSSScaleX,
|
||||
CSSScaleY,
|
||||
CSSRotateZ, // same as rotate()
|
||||
CSSRotate,
|
||||
CSSRotateX,
|
||||
CSSRotateY,
|
||||
CSSRotateZ,
|
||||
CSSSkewX,
|
||||
CSSSkewY,
|
||||
CSSPerspective>;
|
||||
|
||||
@@ -370,24 +370,24 @@ TEST(CSSTransform, scale_y_length) {
|
||||
|
||||
TEST(CSSTransform, rotate_basic) {
|
||||
auto val = parseCSSProperty<CSSTransformFunction>("rotate(90deg)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotateZ>(val));
|
||||
auto& rotate = std::get<CSSRotateZ>(val);
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotate>(val));
|
||||
auto& rotate = std::get<CSSRotate>(val);
|
||||
|
||||
EXPECT_EQ(rotate.degrees, 90.0f);
|
||||
}
|
||||
|
||||
TEST(CSSTransform, rotate_turn) {
|
||||
auto val = parseCSSProperty<CSSTransformFunction>("rotate(1turn)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotateZ>(val));
|
||||
auto& rotate = std::get<CSSRotateZ>(val);
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotate>(val));
|
||||
auto& rotate = std::get<CSSRotate>(val);
|
||||
|
||||
EXPECT_EQ(rotate.degrees, 360.0f);
|
||||
}
|
||||
|
||||
TEST(CSSTransform, rotate_zero) {
|
||||
auto val = parseCSSProperty<CSSTransformFunction>("rotate(0)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotateZ>(val));
|
||||
auto& rotate = std::get<CSSRotateZ>(val);
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotate>(val));
|
||||
auto& rotate = std::get<CSSRotate>(val);
|
||||
|
||||
EXPECT_EQ(rotate.degrees, 0.0f);
|
||||
}
|
||||
@@ -402,8 +402,8 @@ TEST(CSSTransform, rotate_z) {
|
||||
|
||||
TEST(CSSTransform, rotate_funky) {
|
||||
auto val = parseCSSProperty<CSSTransformFunction>("roTate(90deg)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotateZ>(val));
|
||||
auto& rotate = std::get<CSSRotateZ>(val);
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotate>(val));
|
||||
auto& rotate = std::get<CSSRotate>(val);
|
||||
|
||||
EXPECT_EQ(rotate.degrees, 90.0f);
|
||||
}
|
||||
@@ -667,7 +667,7 @@ TEST(CSSTransform, transform_list) {
|
||||
|
||||
EXPECT_EQ(transformList.size(), 3);
|
||||
EXPECT_TRUE(std::holds_alternative<CSSTranslate>(transformList[0]));
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotateZ>(transformList[1]));
|
||||
EXPECT_TRUE(std::holds_alternative<CSSRotate>(transformList[1]));
|
||||
EXPECT_TRUE(std::holds_alternative<CSSScale>(transformList[2]));
|
||||
|
||||
auto& translate = std::get<CSSTranslate>(transformList[0]);
|
||||
@@ -679,7 +679,7 @@ TEST(CSSTransform, transform_list) {
|
||||
EXPECT_EQ(std::get<CSSLength>(translate.x).unit, CSSLengthUnit::Px);
|
||||
EXPECT_EQ(std::get<CSSLength>(translate.y).unit, CSSLengthUnit::Px);
|
||||
|
||||
auto& rotate = std::get<CSSRotateZ>(transformList[1]);
|
||||
auto& rotate = std::get<CSSRotate>(transformList[1]);
|
||||
EXPECT_EQ(rotate.degrees, 90.0f);
|
||||
|
||||
auto& scale = std::get<CSSScale>(transformList[2]);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
/**
|
||||
* Helper to allow passing string literals as template parameters.
|
||||
* https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/
|
||||
*/
|
||||
template <size_t N>
|
||||
struct TemplateStringLiteral {
|
||||
/* implicit */ constexpr TemplateStringLiteral(const char (&str)[N]) {
|
||||
std::copy_n(str, N, value.data());
|
||||
}
|
||||
|
||||
constexpr operator std::string_view() const {
|
||||
return {value.begin(), value.end() - 1};
|
||||
}
|
||||
|
||||
// Not private, since structural types required for template parameters cannot
|
||||
// have non-punlic data members.
|
||||
std::array<char, N> value{};
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
Reference in New Issue
Block a user