Skip to content

Commit

Permalink
Support parsing <shadow> (#48991)
Browse files Browse the repository at this point in the history
Summary:

This adds support for parsing the `<shadow>` data type. In combination with `CSSCommaSeparatedList`, we can now parse box shadow expressions.

Changelog: [Internal]

Reviewed By: lenaic

Differential Revision: D68744811
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Feb 3, 2025
1 parent 94f1423 commit fce5a5c
Show file tree
Hide file tree
Showing 2 changed files with 491 additions and 0 deletions.
152 changes: 152 additions & 0 deletions packages/react-native/ReactCommon/react/renderer/css/CSSShadow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* 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 <optional>
#include <tuple>

#include <react/renderer/css/CSSColor.h>
#include <react/renderer/css/CSSCommaSeparatedList.h>
#include <react/renderer/css/CSSDataType.h>
#include <react/renderer/css/CSSKeyword.h>
#include <react/renderer/css/CSSLength.h>
#include <react/renderer/css/CSSValueParser.h>
#include <react/utils/to_underlying.h>

namespace facebook::react {

/**
* Representation of CSS <shadow> data type
* https://drafts.csswg.org/css-backgrounds/#typedef-shadow
*/
struct CSSShadow {
CSSLength offsetX{};
CSSLength offsetY{};
CSSLength blurRadius{};
CSSLength spreadDistance{};
CSSColor color{CSSColor::black()};
bool inset{false};

constexpr bool operator==(const CSSShadow& rhs) const = default;
};

/**
* Represents a keyword for an inset shadow.
*/
enum class CSSInsetShadowKeyword : std::underlying_type_t<CSSKeyword> {
Inset = to_underlying(CSSKeyword::Inset),
};

static_assert(CSSDataType<CSSInsetShadowKeyword>);

template <>
struct CSSDataTypeParser<CSSShadow> {
static constexpr auto consume(CSSSyntaxParser& parser)
-> std::optional<CSSShadow> {
std::optional<CSSColor> color{};
bool inset{false};
std::optional<std::tuple<CSSLength, CSSLength, CSSLength, CSSLength>>
lengths{};

for (auto nextValue =
parseNextCSSValue<CSSLength, CSSColor, CSSInsetShadowKeyword>(
parser);
!std::holds_alternative<std::monostate>(nextValue);
nextValue =
parseNextCSSValue<CSSLength, CSSColor, CSSInsetShadowKeyword>(
parser, CSSDelimiter::Whitespace)) {
if (std::holds_alternative<CSSLength>(nextValue)) {
if (lengths.has_value()) {
return {};
}
lengths = parseRestLengths(std::get<CSSLength>(nextValue), parser);
if (!lengths.has_value()) {
return {};
}
continue;
}

if (std::holds_alternative<CSSColor>(nextValue)) {
if (color.has_value()) {
return {};
}
color = std::get<CSSColor>(nextValue);
continue;
}

if (std::holds_alternative<CSSInsetShadowKeyword>(nextValue)) {
if (inset) {
return {};
}
inset = true;
continue;
}
}

if (!lengths.has_value()) {
return {};
}

return CSSShadow{
.offsetX = std::get<0>(*lengths),
.offsetY = std::get<1>(*lengths),
.blurRadius = std::get<2>(*lengths),
.spreadDistance = std::get<3>(*lengths),
.color = color.value_or(CSSColor::black()),
.inset = inset,
};
}

private:
static constexpr auto parseRestLengths(
CSSLength offsetX,
CSSSyntaxParser& parser)
-> std::optional<std::tuple<CSSLength, CSSLength, CSSLength, CSSLength>> {
auto offsetY =
parseNextCSSValue<CSSLength>(parser, CSSDelimiter::Whitespace);
if (std::holds_alternative<std::monostate>(offsetY)) {
return {};
}

auto blurRadius =
parseNextCSSValue<CSSLength>(parser, CSSDelimiter::Whitespace);
if (std::holds_alternative<std::monostate>(blurRadius)) {
return std::make_tuple(
offsetX, std::get<CSSLength>(offsetY), CSSLength{}, CSSLength{});
}
if (std::get<CSSLength>(blurRadius).value < 0) {
return {};
}

auto spreadDistance =
parseNextCSSValue<CSSLength>(parser, CSSDelimiter::Whitespace);
if (std::holds_alternative<std::monostate>(spreadDistance)) {
return std::make_tuple(
offsetX,
std::get<CSSLength>(offsetY),
std::get<CSSLength>(blurRadius),
CSSLength{});
}

return std::make_tuple(
offsetX,
std::get<CSSLength>(offsetY),
std::get<CSSLength>(blurRadius),
std::get<CSSLength>(spreadDistance));
}
};

static_assert(CSSDataType<CSSShadow>);

/**
* Represents a comma separated list of at least one <shadow>
* <shadow>#
*/
using CSSShadowList = CSSCommaSeparatedList<CSSShadow>;

} // namespace facebook::react
Loading

0 comments on commit fce5a5c

Please sign in to comment.