mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add <hex-color> to CSSParser (#44624)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44624 The syntax for <color> is defined as (https://www.w3.org/TR/css-color-4/#color-syntax): ``` <color> = <color-base> | currentColor | <system-color> <color-base> = <hex-color> | <color-function> | <named-color> | transparent <color-function> = <rgb()> | <rgba()> | <hsl()> | <hsla()> | <hwb()> | <lab()> | <lch()> | <oklab()> | <oklch()> | <color()> ``` This diff implements in particular: ``` <color-base> = <hex-color> ``` We parse over a `<hash-token>` to extract a 3, 4, 6 or 8 digit long hexadecimal and parse it into a CSSColor struct representation: ``` struct Color { uint8_t r // 0-255 uint8_t g // 0-255 uint8_t b // 0-255 uint8_t a // 0.0 - 1.0 } ``` Changelog: [Internal] Reviewed By: NickGerleman Differential Revision: D57287309 fbshipit-source-id: 35ce82e52fdc8fc86425e03d7883ae09b429b9e4
This commit is contained in:
committed by
Facebook GitHub Bot
parent
7349dabae2
commit
417ff5fec9
@@ -27,6 +27,7 @@ enum class CSSValueType : uint8_t {
|
||||
Percentage,
|
||||
Ratio,
|
||||
Angle,
|
||||
Color,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -81,4 +82,11 @@ struct CSSAngle {
|
||||
float degrees{};
|
||||
};
|
||||
|
||||
struct CSSColor {
|
||||
uint8_t r{};
|
||||
uint8_t g{};
|
||||
uint8_t b{};
|
||||
uint8_t a{};
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -76,7 +76,12 @@ class CSSValueParser {
|
||||
return *percentage;
|
||||
}
|
||||
}
|
||||
|
||||
// <color>
|
||||
if constexpr (hasType<CSSColor>()) {
|
||||
if (auto colorValue = consumeColorToken(token)) {
|
||||
return *colorValue;
|
||||
}
|
||||
}
|
||||
return CSSValue{};
|
||||
});
|
||||
// TODO: support function component values and simple blocks
|
||||
@@ -224,6 +229,93 @@ class CSSValueParser {
|
||||
value != -std::numeric_limits<float>::infinity();
|
||||
}
|
||||
|
||||
enum class HexColorType {
|
||||
Long,
|
||||
Short,
|
||||
};
|
||||
|
||||
constexpr std::optional<CSSValue> consumeColorToken(
|
||||
const CSSPreservedToken& token) {
|
||||
// https://www.w3.org/TR/css-color-4/#hex-color
|
||||
std::string_view hexColorValue = token.stringValue();
|
||||
if (isValidHexColor(hexColorValue)) {
|
||||
if (hexColorValue.length() == 3) {
|
||||
return CSSValue::color(
|
||||
hexToNumeric(hexColorValue.substr(0, 1), HexColorType::Short),
|
||||
hexToNumeric(hexColorValue.substr(1, 1), HexColorType::Short),
|
||||
hexToNumeric(hexColorValue.substr(2, 1), HexColorType::Short),
|
||||
255u);
|
||||
} else if (hexColorValue.length() == 4) {
|
||||
return CSSValue::color(
|
||||
hexToNumeric(hexColorValue.substr(0, 1), HexColorType::Short),
|
||||
hexToNumeric(hexColorValue.substr(1, 1), HexColorType::Short),
|
||||
hexToNumeric(hexColorValue.substr(2, 1), HexColorType::Short),
|
||||
hexToNumeric(hexColorValue.substr(3, 1), HexColorType::Short));
|
||||
} else if (hexColorValue.length() == 6) {
|
||||
return CSSValue::color(
|
||||
hexToNumeric(hexColorValue.substr(0, 2), HexColorType::Long),
|
||||
hexToNumeric(hexColorValue.substr(2, 2), HexColorType::Long),
|
||||
hexToNumeric(hexColorValue.substr(4, 2), HexColorType::Long),
|
||||
255u);
|
||||
} else if (hexColorValue.length() == 8) {
|
||||
return CSSValue::color(
|
||||
hexToNumeric(hexColorValue.substr(0, 2), HexColorType::Long),
|
||||
hexToNumeric(hexColorValue.substr(2, 2), HexColorType::Long),
|
||||
hexToNumeric(hexColorValue.substr(4, 2), HexColorType::Long),
|
||||
hexToNumeric(hexColorValue.substr(6, 2), HexColorType::Long));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr char toLower(char c) {
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return static_cast<char>(c + 32);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
constexpr uint8_t hexToNumeric(std::string_view hex, HexColorType hexType) {
|
||||
int result = 0;
|
||||
for (char c : hex) {
|
||||
int value = 0;
|
||||
if (c >= '0' && c <= '9') {
|
||||
value = c - '0';
|
||||
} else {
|
||||
value = toLower(c) - 'a' + 10;
|
||||
}
|
||||
result *= 16;
|
||||
result += value;
|
||||
}
|
||||
|
||||
if (hexType == HexColorType::Short) {
|
||||
return result * 16 + result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool isValidHexColor(std::string_view hex) {
|
||||
// The syntax of a <hex-color> is a <hash-token> token whose value consists
|
||||
// of 3, 4, 6, or 8 hexadecimal digits.
|
||||
if (hex.size() != 3 && hex.size() != 4 && hex.size() != 6 &&
|
||||
hex.size() != 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto c : hex) {
|
||||
if (!isHexDigit(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool isHexDigit(char c) {
|
||||
return (c >= '0' && c <= '9') || (toLower(c) >= 'a' && toLower(c) <= 'f');
|
||||
}
|
||||
|
||||
CSSSyntaxParser parser_;
|
||||
};
|
||||
|
||||
|
||||
@@ -109,6 +109,13 @@ class CSSValueVariant {
|
||||
return CSSValueVariant(CSSValueType::Angle, CSSAngle{degrees});
|
||||
}
|
||||
|
||||
static constexpr CSSValueVariant
|
||||
color(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
requires(canRepresent<CSSColor>())
|
||||
{
|
||||
return CSSValueVariant(CSSValueType::Color, CSSColor{r, g, b, a});
|
||||
}
|
||||
|
||||
constexpr CSSValueType type() const {
|
||||
return type_;
|
||||
}
|
||||
@@ -155,6 +162,12 @@ class CSSValueVariant {
|
||||
return getIf<CSSValueType::Angle, CSSAngle>();
|
||||
}
|
||||
|
||||
constexpr CSSColor getColor() const
|
||||
requires(canRepresent<CSSColor>())
|
||||
{
|
||||
return getIf<CSSValueType::Color, CSSColor>();
|
||||
}
|
||||
|
||||
constexpr bool hasValue() const
|
||||
requires(canRepresent<CSSWideKeyword>())
|
||||
{
|
||||
@@ -185,6 +198,8 @@ class CSSValueVariant {
|
||||
return getRatio() == other.getRatio();
|
||||
case CSSValueType::Angle:
|
||||
return getAngle() == other.getAngle();
|
||||
case CSSValueType::Color:
|
||||
return getColor() == other.getColor();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -320,4 +320,61 @@ TEST(CSSValueParser, parse_length_prop_constexpr) {
|
||||
EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px);
|
||||
}
|
||||
|
||||
TEST(CSSValueParser, hex_color_values) {
|
||||
auto emptyValue = parseCSSValue<CSSWideKeyword, CSSColor>("");
|
||||
EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword);
|
||||
EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset);
|
||||
|
||||
auto hex3DigitColorValue = parseCSSValue<CSSWideKeyword, CSSColor>("#fff");
|
||||
EXPECT_EQ(hex3DigitColorValue.type(), CSSValueType::Color);
|
||||
EXPECT_EQ(hex3DigitColorValue.getColor().r, 255);
|
||||
EXPECT_EQ(hex3DigitColorValue.getColor().g, 255);
|
||||
EXPECT_EQ(hex3DigitColorValue.getColor().b, 255);
|
||||
EXPECT_EQ(hex3DigitColorValue.getColor().a, 255);
|
||||
|
||||
auto hex4DigitColorValue = parseCSSValue<CSSWideKeyword, CSSColor>("#ffff");
|
||||
EXPECT_EQ(hex4DigitColorValue.type(), CSSValueType::Color);
|
||||
EXPECT_EQ(hex4DigitColorValue.getColor().r, 255);
|
||||
EXPECT_EQ(hex4DigitColorValue.getColor().g, 255);
|
||||
EXPECT_EQ(hex4DigitColorValue.getColor().b, 255);
|
||||
EXPECT_EQ(hex4DigitColorValue.getColor().a, 255);
|
||||
|
||||
auto hex6DigitColorValue = parseCSSValue<CSSWideKeyword, CSSColor>("#ffffff");
|
||||
EXPECT_EQ(hex6DigitColorValue.type(), CSSValueType::Color);
|
||||
EXPECT_EQ(hex6DigitColorValue.getColor().r, 255);
|
||||
EXPECT_EQ(hex6DigitColorValue.getColor().g, 255);
|
||||
EXPECT_EQ(hex6DigitColorValue.getColor().b, 255);
|
||||
EXPECT_EQ(hex6DigitColorValue.getColor().a, 255);
|
||||
|
||||
auto hex8DigitColorValue =
|
||||
parseCSSValue<CSSWideKeyword, CSSColor>("#ffffffff");
|
||||
EXPECT_EQ(hex8DigitColorValue.type(), CSSValueType::Color);
|
||||
EXPECT_EQ(hex8DigitColorValue.getColor().r, 255);
|
||||
EXPECT_EQ(hex8DigitColorValue.getColor().g, 255);
|
||||
EXPECT_EQ(hex8DigitColorValue.getColor().b, 255);
|
||||
EXPECT_EQ(hex8DigitColorValue.getColor().a, 255);
|
||||
|
||||
auto hexMixedCaseColorValue =
|
||||
parseCSSValue<CSSWideKeyword, CSSColor>("#FFCc99");
|
||||
EXPECT_EQ(hexMixedCaseColorValue.type(), CSSValueType::Color);
|
||||
EXPECT_EQ(hexMixedCaseColorValue.getColor().r, 255);
|
||||
EXPECT_EQ(hexMixedCaseColorValue.getColor().g, 204);
|
||||
EXPECT_EQ(hexMixedCaseColorValue.getColor().b, 153);
|
||||
EXPECT_EQ(hexMixedCaseColorValue.getColor().a, 255);
|
||||
|
||||
auto hexDigitOnlyColorValue = parseCSSValue<CSSWideKeyword, CSSColor>("#369");
|
||||
EXPECT_EQ(hexDigitOnlyColorValue.type(), CSSValueType::Color);
|
||||
EXPECT_EQ(hexDigitOnlyColorValue.getColor().r, 51);
|
||||
EXPECT_EQ(hexDigitOnlyColorValue.getColor().g, 102);
|
||||
EXPECT_EQ(hexDigitOnlyColorValue.getColor().b, 153);
|
||||
EXPECT_EQ(hexDigitOnlyColorValue.getColor().a, 255);
|
||||
|
||||
auto hexAlphaTestValue = parseCSSValue<CSSWideKeyword, CSSColor>("#FFFFFFCC");
|
||||
EXPECT_EQ(hexAlphaTestValue.type(), CSSValueType::Color);
|
||||
EXPECT_EQ(hexAlphaTestValue.getColor().r, 255);
|
||||
EXPECT_EQ(hexAlphaTestValue.getColor().g, 255);
|
||||
EXPECT_EQ(hexAlphaTestValue.getColor().b, 255);
|
||||
EXPECT_EQ(hexAlphaTestValue.getColor().a, 204);
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
Reference in New Issue
Block a user