mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add CSSValue support for <angle> data type (#44430)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44430 Let's add support for angles so we can correctly represent things like rotation/skew transforms or hue-rotate filter. This should replace `ValueUnit` in prop related transform code. A couple implementation notes: 1. RN currently uses radians internally, but CSS says the cannonical angle unit is degrees, so we keep to that 2. We have all the information to convert to cannonical value type at parsing layer, so we do that, and clients can only see degreee values instead of units. Less flexible, but simpler/more efficient for now, where higher levels don't care. Changelog: [Internal] Reviewed By: sammy-SC Differential Revision: D57029378 fbshipit-source-id: 91341f1bf4686d9016823ac8cf91897e933345f9
This commit is contained in:
committed by
Facebook GitHub Bot
parent
40b54ee671
commit
5c79e99ce8
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 <cmath>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include <react/utils/fnv1a.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
/**
|
||||
* Unit for the CSS <angle> type.
|
||||
* https://www.w3.org/TR/css-values-4/#angles
|
||||
*/
|
||||
enum class CSSAngleUnit : uint8_t {
|
||||
Deg,
|
||||
Grad,
|
||||
Rad,
|
||||
Turn,
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a unit from a dimension token into a CSS angle unit.
|
||||
*/
|
||||
constexpr std::optional<CSSAngleUnit> parseCSSAngleUnit(std::string_view unit) {
|
||||
switch (fnv1a(unit)) {
|
||||
case fnv1a("deg"):
|
||||
return CSSAngleUnit::Deg;
|
||||
case fnv1a("grad"):
|
||||
return CSSAngleUnit::Grad;
|
||||
case fnv1a("rad"):
|
||||
return CSSAngleUnit::Rad;
|
||||
case fnv1a("turn"):
|
||||
return CSSAngleUnit::Turn;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a specified CSS angle to its cannonical unit (degrees)
|
||||
*/
|
||||
constexpr float canonicalize(float value, CSSAngleUnit unit) {
|
||||
switch (unit) {
|
||||
case CSSAngleUnit::Deg:
|
||||
return value;
|
||||
case CSSAngleUnit::Grad:
|
||||
return value * 0.9f;
|
||||
case CSSAngleUnit::Rad:
|
||||
return value * 57.295779513f;
|
||||
case CSSAngleUnit::Turn:
|
||||
return value * 360.0f;
|
||||
default:
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
@@ -93,7 +93,8 @@ class CSSDeclaredStyle {
|
||||
CSSLength,
|
||||
CSSNumber,
|
||||
CSSPercentage,
|
||||
CSSRatio>)>
|
||||
CSSRatio,
|
||||
CSSAngle>)>
|
||||
value;
|
||||
|
||||
constexpr bool operator<(const PropMapping& rhs) const {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <react/renderer/css/CSSAngleUnit.h>
|
||||
#include <react/renderer/css/CSSKeywords.h>
|
||||
#include <react/renderer/css/CSSLengthUnit.h>
|
||||
#include <react/renderer/css/CSSProperties.h>
|
||||
@@ -107,6 +108,12 @@ class CSSParser {
|
||||
return CSSValueT::length(consumeToken().numericValue(), *unit);
|
||||
}
|
||||
}
|
||||
if constexpr (traits::containsType<CSSAngle, AllowedTypesT...>()) {
|
||||
if (auto unit = parseCSSAngleUnit(peek().unit())) {
|
||||
return CSSValueT::angle(
|
||||
canonicalize(consumeToken().numericValue(), *unit));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ enum class CSSValueType : uint8_t {
|
||||
Number,
|
||||
Percentage,
|
||||
Ratio,
|
||||
Angle,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -74,6 +75,14 @@ struct CSSRatio {
|
||||
float denominator{};
|
||||
};
|
||||
|
||||
/**
|
||||
* Representation of CSS <angle> data type
|
||||
* https://www.w3.org/TR/css-values-4/#angles
|
||||
*/
|
||||
struct CSSAngle {
|
||||
float degrees{};
|
||||
};
|
||||
|
||||
/**
|
||||
* CSSValueVariant represents a CSS component value:
|
||||
* https://www.w3.org/TR/css-values-4/#component-types
|
||||
@@ -161,6 +170,12 @@ class CSSValueVariant {
|
||||
CSSValueType::Ratio, CSSRatio{numerator, denominator});
|
||||
}
|
||||
|
||||
static constexpr CSSValueVariant angle(float degrees)
|
||||
requires(canRepresent<CSSAngle>())
|
||||
{
|
||||
return CSSValueVariant(CSSValueType::Angle, CSSAngle{degrees});
|
||||
}
|
||||
|
||||
constexpr CSSValueType type() const {
|
||||
return type_;
|
||||
}
|
||||
@@ -201,6 +216,12 @@ class CSSValueVariant {
|
||||
return getIf<CSSValueType::Ratio, CSSRatio>();
|
||||
}
|
||||
|
||||
constexpr CSSAngle getAngle() const
|
||||
requires(canRepresent<CSSAngle>())
|
||||
{
|
||||
return getIf<CSSValueType::Angle, CSSAngle>();
|
||||
}
|
||||
|
||||
constexpr operator bool() const
|
||||
requires(canRepresent<CSSWideKeyword>())
|
||||
{
|
||||
@@ -225,6 +246,8 @@ class CSSValueVariant {
|
||||
return getPercentage() == other.getPercentage();
|
||||
case CSSValueType::Ratio:
|
||||
return getRatio() == other.getRatio();
|
||||
case CSSValueType::Angle:
|
||||
return getAngle() == other.getAngle();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -267,6 +267,34 @@ TEST(CSSParser, number_ratio_values) {
|
||||
EXPECT_EQ(degenerateRatio.getNumber().value, 0.0f);
|
||||
}
|
||||
|
||||
TEST(CSSParser, angle_values) {
|
||||
auto emptyValue = parseCSSComponentValue<CSSWideKeyword, CSSAngle>("");
|
||||
EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword);
|
||||
EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset);
|
||||
|
||||
auto degreeValue = parseCSSComponentValue<CSSWideKeyword, CSSAngle>("10deg");
|
||||
EXPECT_EQ(degreeValue.type(), CSSValueType::Angle);
|
||||
EXPECT_EQ(degreeValue.getAngle().degrees, 10.0f);
|
||||
|
||||
auto radianValue = parseCSSComponentValue<CSSWideKeyword, CSSAngle>("10rad");
|
||||
EXPECT_EQ(radianValue.type(), CSSValueType::Angle);
|
||||
EXPECT_NEAR(radianValue.getAngle().degrees, 572.958f, 0.001f);
|
||||
|
||||
auto negativeRadianValue =
|
||||
parseCSSComponentValue<CSSWideKeyword, CSSAngle>("-10rad");
|
||||
EXPECT_EQ(negativeRadianValue.type(), CSSValueType::Angle);
|
||||
EXPECT_NEAR(negativeRadianValue.getAngle().degrees, -572.958f, 0.001f);
|
||||
|
||||
auto gradianValue =
|
||||
parseCSSComponentValue<CSSWideKeyword, CSSAngle>("10grad");
|
||||
EXPECT_EQ(gradianValue.type(), CSSValueType::Angle);
|
||||
ASSERT_NEAR(gradianValue.getAngle().degrees, 9.0f, 0.001f);
|
||||
|
||||
auto turnValue = parseCSSComponentValue<CSSWideKeyword, CSSAngle>("1turn");
|
||||
EXPECT_EQ(turnValue.type(), CSSValueType::Angle);
|
||||
EXPECT_EQ(turnValue.getAngle().degrees, 360.0f);
|
||||
}
|
||||
|
||||
TEST(CSSParser, parse_prop) {
|
||||
auto emptyValue = parseCSSProp<CSSProp::Width>("");
|
||||
EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword);
|
||||
|
||||
Reference in New Issue
Block a user