This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

View File

@ -0,0 +1,185 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/core/ConcreteComponentDescriptor.h>
#include "AndroidTextInputShadowNode.h"
#include <yoga/CompactValue.h>
#include <yoga/YGEnums.h>
#include <yoga/YGValue.h>
namespace facebook {
namespace react {
/*
* Descriptor for <AndroidTextInput> component.
*/
class AndroidTextInputComponentDescriptor final
: public ConcreteComponentDescriptor<AndroidTextInputShadowNode> {
public:
AndroidTextInputComponentDescriptor(
ComponentDescriptorParameters const &parameters)
: ConcreteComponentDescriptor<AndroidTextInputShadowNode>(parameters) {
// Every single `AndroidTextInputShadowNode` will have a reference to
// a shared `TextLayoutManager`.
textLayoutManager_ = std::make_shared<TextLayoutManager>(contextContainer_);
}
virtual State::Shared createInitialState(
ShadowNodeFragment const &fragment,
ShadowNodeFamily::Shared const &family) const override {
int surfaceId = family->getSurfaceId();
float defaultThemePaddingStart = NAN;
float defaultThemePaddingEnd = NAN;
float defaultThemePaddingTop = NAN;
float defaultThemePaddingBottom = NAN;
if (surfaceIdToThemePaddingMap_.find(surfaceId) !=
surfaceIdToThemePaddingMap_.end()) {
YGStyle::Edges theme = surfaceIdToThemePaddingMap_[surfaceId];
defaultThemePaddingStart = ((YGValue)theme[YGEdgeStart]).value;
defaultThemePaddingEnd = ((YGValue)theme[YGEdgeEnd]).value;
defaultThemePaddingTop = ((YGValue)theme[YGEdgeTop]).value;
defaultThemePaddingBottom = ((YGValue)theme[YGEdgeBottom]).value;
}
return std::make_shared<AndroidTextInputShadowNode::ConcreteState>(
std::make_shared<AndroidTextInputState const>(AndroidTextInputState(
0,
{},
{},
{},
{},
{},
textLayoutManager_,
defaultThemePaddingStart,
defaultThemePaddingEnd,
defaultThemePaddingTop,
defaultThemePaddingBottom)),
family);
}
protected:
void adopt(UnsharedShadowNode shadowNode) const override {
assert(std::dynamic_pointer_cast<AndroidTextInputShadowNode>(shadowNode));
auto textInputShadowNode =
std::static_pointer_cast<AndroidTextInputShadowNode>(shadowNode);
// `ParagraphShadowNode` uses `TextLayoutManager` to measure text content
// and communicate text rendering metrics to mounting layer.
textInputShadowNode->setTextLayoutManager(textLayoutManager_);
textInputShadowNode->setContextContainer(
const_cast<ContextContainer *>(getContextContainer().get()));
// Get theme padding from cache, or set it from State.
// In theory, the Java ViewManager for TextInput should need to set state
// *exactly once* per surface to communicate the correct default padding,
// which will be cached here in C++.
// TODO T63008435: can this feature be removed entirely?
// TODO: figure out RTL/start/end/left/right stuff here
int surfaceId = textInputShadowNode->getSurfaceId();
const AndroidTextInputState &state = textInputShadowNode->getStateData();
if (surfaceIdToThemePaddingMap_.find(surfaceId) ==
surfaceIdToThemePaddingMap_.end() &&
!isnan(state.defaultThemePaddingStart)) {
YGStyle::Edges result;
result[YGEdgeStart] =
(YGValue){state.defaultThemePaddingStart, YGUnitPoint};
result[YGEdgeEnd] = (YGValue){state.defaultThemePaddingEnd, YGUnitPoint};
result[YGEdgeTop] = (YGValue){state.defaultThemePaddingTop, YGUnitPoint};
result[YGEdgeBottom] =
(YGValue){state.defaultThemePaddingBottom, YGUnitPoint};
surfaceIdToThemePaddingMap_.emplace(std::make_pair(surfaceId, result));
}
if (surfaceIdToThemePaddingMap_.find(surfaceId) !=
surfaceIdToThemePaddingMap_.end()) {
YGStyle::Edges theme = surfaceIdToThemePaddingMap_[surfaceId];
// Override padding
// Node is still unsealed during adoption, before layout is complete
// TODO: T62959168 account for RTL and paddingLeft when setting default
// paddingStart, and vice-versa with paddingRight/paddingEnd.
// For now this assumes no RTL.
YGStyle::Edges result =
textInputShadowNode->getConcreteProps().yogaStyle.padding();
bool changedPadding = false;
if (!textInputShadowNode->getConcreteProps().hasPadding &&
!textInputShadowNode->getConcreteProps().hasPaddingStart &&
!textInputShadowNode->getConcreteProps().hasPaddingLeft &&
!textInputShadowNode->getConcreteProps().hasPaddingHorizontal) {
changedPadding = true;
result[YGEdgeStart] = theme[YGEdgeStart];
}
if (!textInputShadowNode->getConcreteProps().hasPadding &&
!textInputShadowNode->getConcreteProps().hasPaddingEnd &&
!textInputShadowNode->getConcreteProps().hasPaddingRight &&
!textInputShadowNode->getConcreteProps().hasPaddingHorizontal) {
changedPadding = true;
result[YGEdgeEnd] = theme[YGEdgeEnd];
}
if (!textInputShadowNode->getConcreteProps().hasPadding &&
!textInputShadowNode->getConcreteProps().hasPaddingTop &&
!textInputShadowNode->getConcreteProps().hasPaddingVertical) {
changedPadding = true;
result[YGEdgeTop] = theme[YGEdgeTop];
}
if (!textInputShadowNode->getConcreteProps().hasPadding &&
!textInputShadowNode->getConcreteProps().hasPaddingBottom &&
!textInputShadowNode->getConcreteProps().hasPaddingVertical) {
changedPadding = true;
result[YGEdgeBottom] = theme[YGEdgeBottom];
}
// If the TextInput initially does not have paddingLeft or paddingStart, a
// paddingStart may be set from the theme. If that happens, when there's a
// paddingLeft update, we must explicitly unset paddingStart... (same with
// paddingEnd)
// TODO: support RTL
if ((textInputShadowNode->getConcreteProps().hasPadding ||
textInputShadowNode->getConcreteProps().hasPaddingLeft ||
textInputShadowNode->getConcreteProps().hasPaddingHorizontal) &&
!textInputShadowNode->getConcreteProps().hasPaddingStart) {
result[YGEdgeStart] = YGValueUndefined;
}
if ((textInputShadowNode->getConcreteProps().hasPadding ||
textInputShadowNode->getConcreteProps().hasPaddingRight ||
textInputShadowNode->getConcreteProps().hasPaddingHorizontal) &&
!textInputShadowNode->getConcreteProps().hasPaddingEnd) {
result[YGEdgeEnd] = YGValueUndefined;
}
// Note that this is expensive: on every adopt, we need to set the Yoga
// props again, which normally only happens during prop parsing. Every
// commit, state update, etc, will incur this cost.
if (changedPadding) {
// Set new props on node
const_cast<AndroidTextInputProps &>(
textInputShadowNode->getConcreteProps())
.yogaStyle.padding() = result;
// Communicate new props to Yoga part of the node
textInputShadowNode->updateYogaProps();
}
}
textInputShadowNode->dirtyLayout();
textInputShadowNode->enableMeasurement();
ConcreteComponentDescriptor::adopt(shadowNode);
}
private:
SharedTextLayoutManager textLayoutManager_;
mutable better::map<int, YGStyle::Edges> surfaceIdToThemePaddingMap_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AndroidTextInputEventEmitter.h"
namespace facebook {
namespace react {
void AndroidTextInputEventEmitter::onBlur(
AndroidTextInputOnBlurStruct event) const {
dispatchEvent("blur", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
return payload;
});
}
void AndroidTextInputEventEmitter::onFocus(
AndroidTextInputOnFocusStruct event) const {
dispatchEvent("focus", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
return payload;
});
}
void AndroidTextInputEventEmitter::onChange(
AndroidTextInputOnChangeStruct event) const {
dispatchEvent("change", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "eventCount", event.eventCount);
payload.setProperty(runtime, "text", event.text);
return payload;
});
}
void AndroidTextInputEventEmitter::onChangeText(
AndroidTextInputOnChangeTextStruct event) const {
dispatchEvent(
"changeText", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "eventCount", event.eventCount);
payload.setProperty(runtime, "text", event.text);
return payload;
});
}
void AndroidTextInputEventEmitter::onContentSizeChange(
AndroidTextInputOnContentSizeChangeStruct event) const {
dispatchEvent(
"contentSizeChange", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
{
auto contentSize = jsi::Object(runtime);
contentSize.setProperty(runtime, "width", event.contentSize.width);
contentSize.setProperty(runtime, "height", event.contentSize.height);
payload.setProperty(runtime, "contentSize", contentSize);
}
return payload;
});
}
void AndroidTextInputEventEmitter::onTextInput(
AndroidTextInputOnTextInputStruct event) const {
dispatchEvent("textInput", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "text", event.text);
payload.setProperty(runtime, "previousText", event.previousText);
{
auto range = jsi::Object(runtime);
range.setProperty(runtime, "start", event.range.start);
range.setProperty(runtime, "end", event.range.end);
payload.setProperty(runtime, "range", range);
}
return payload;
});
}
void AndroidTextInputEventEmitter::onEndEditing(
AndroidTextInputOnEndEditingStruct event) const {
dispatchEvent(
"endEditing", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "text", event.text);
return payload;
});
}
void AndroidTextInputEventEmitter::onSelectionChange(
AndroidTextInputOnSelectionChangeStruct event) const {
dispatchEvent(
"selectionChange", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
{
auto selection = jsi::Object(runtime);
selection.setProperty(runtime, "start", event.selection.start);
selection.setProperty(runtime, "end", event.selection.end);
payload.setProperty(runtime, "selection", selection);
}
return payload;
});
}
void AndroidTextInputEventEmitter::onSubmitEditing(
AndroidTextInputOnSubmitEditingStruct event) const {
dispatchEvent(
"submitEditing", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "text", event.text);
return payload;
});
}
void AndroidTextInputEventEmitter::onKeyPress(
AndroidTextInputOnKeyPressStruct event) const {
dispatchEvent("keyPress", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(runtime, "key", event.key);
return payload;
});
}
void AndroidTextInputEventEmitter::onScroll(
AndroidTextInputOnScrollStruct event) const {
dispatchEvent("scroll", [event = std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "target", event.target);
payload.setProperty(
runtime, "responderIgnoreScroll", event.responderIgnoreScroll);
{
auto contentInset = jsi::Object(runtime);
contentInset.setProperty(runtime, "top", event.contentInset.top);
contentInset.setProperty(runtime, "bottom", event.contentInset.bottom);
contentInset.setProperty(runtime, "left", event.contentInset.left);
contentInset.setProperty(runtime, "right", event.contentInset.right);
payload.setProperty(runtime, "contentInset", contentInset);
}
{
auto contentOffset = jsi::Object(runtime);
contentOffset.setProperty(runtime, "x", event.contentOffset.x);
contentOffset.setProperty(runtime, "y", event.contentOffset.y);
payload.setProperty(runtime, "contentOffset", contentOffset);
}
{
auto contentSize = jsi::Object(runtime);
contentSize.setProperty(runtime, "width", event.contentSize.width);
contentSize.setProperty(runtime, "height", event.contentSize.height);
payload.setProperty(runtime, "contentSize", contentSize);
}
{
auto layoutMeasurement = jsi::Object(runtime);
layoutMeasurement.setProperty(
runtime, "width", event.layoutMeasurement.width);
layoutMeasurement.setProperty(
runtime, "height", event.layoutMeasurement.height);
payload.setProperty(runtime, "layoutMeasurement", layoutMeasurement);
}
{
auto velocity = jsi::Object(runtime);
velocity.setProperty(runtime, "x", event.velocity.x);
velocity.setProperty(runtime, "y", event.velocity.y);
payload.setProperty(runtime, "velocity", velocity);
}
return payload;
});
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/components/view/ViewEventEmitter.h>
namespace facebook {
namespace react {
struct AndroidTextInputOnBlurStruct {
int target;
};
struct AndroidTextInputOnFocusStruct {
int target;
};
struct AndroidTextInputOnChangeStruct {
int target;
int eventCount;
std::string text;
};
struct AndroidTextInputOnChangeTextStruct {
int target;
int eventCount;
std::string text;
};
struct AndroidTextInputOnContentSizeChangeContentSizeStruct {
double width;
double height;
};
struct AndroidTextInputOnContentSizeChangeStruct {
int target;
AndroidTextInputOnContentSizeChangeContentSizeStruct contentSize;
};
struct AndroidTextInputOnTextInputRangeStruct {
double start;
double end;
};
struct AndroidTextInputOnTextInputStruct {
int target;
std::string text;
std::string previousText;
AndroidTextInputOnTextInputRangeStruct range;
};
struct AndroidTextInputOnEndEditingStruct {
int target;
std::string text;
};
struct AndroidTextInputOnSelectionChangeSelectionStruct {
double start;
double end;
};
struct AndroidTextInputOnSelectionChangeStruct {
int target;
AndroidTextInputOnSelectionChangeSelectionStruct selection;
};
struct AndroidTextInputOnSubmitEditingStruct {
int target;
std::string text;
};
struct AndroidTextInputOnKeyPressStruct {
int target;
std::string key;
};
struct AndroidTextInputOnScrollContentInsetStruct {
double top;
double bottom;
double left;
double right;
};
struct AndroidTextInputOnScrollContentOffsetStruct {
double x;
double y;
};
struct AndroidTextInputOnScrollContentSizeStruct {
double width;
double height;
};
struct AndroidTextInputOnScrollLayoutMeasurementStruct {
double width;
double height;
};
struct AndroidTextInputOnScrollVelocityStruct {
double x;
double y;
};
struct AndroidTextInputOnScrollStruct {
int target;
bool responderIgnoreScroll;
AndroidTextInputOnScrollContentInsetStruct contentInset;
AndroidTextInputOnScrollContentOffsetStruct contentOffset;
AndroidTextInputOnScrollContentSizeStruct contentSize;
AndroidTextInputOnScrollLayoutMeasurementStruct layoutMeasurement;
AndroidTextInputOnScrollVelocityStruct velocity;
};
class AndroidTextInputEventEmitter : public ViewEventEmitter {
public:
using ViewEventEmitter::ViewEventEmitter;
void onBlur(AndroidTextInputOnBlurStruct value) const;
void onFocus(AndroidTextInputOnFocusStruct value) const;
void onChange(AndroidTextInputOnChangeStruct value) const;
void onChangeText(AndroidTextInputOnChangeTextStruct value) const;
void onContentSizeChange(
AndroidTextInputOnContentSizeChangeStruct value) const;
void onTextInput(AndroidTextInputOnTextInputStruct value) const;
void onEndEditing(AndroidTextInputOnEndEditingStruct value) const;
void onSelectionChange(AndroidTextInputOnSelectionChangeStruct value) const;
void onSubmitEditing(AndroidTextInputOnSubmitEditingStruct value) const;
void onKeyPress(AndroidTextInputOnKeyPressStruct value) const;
void onScroll(AndroidTextInputOnScrollStruct value) const;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,370 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AndroidTextInputProps.h"
#include <react/components/image/conversions.h>
#include <react/core/propsConversions.h>
#include <react/graphics/conversions.h>
namespace facebook {
namespace react {
static bool hasValue(
const RawProps &rawProps,
bool defaultValue,
const char *name,
const char *prefix,
const char *suffix) {
auto rawValue = rawProps.at(name, prefix, suffix);
// No change to prop - use default
if (rawValue == nullptr) {
return defaultValue;
}
// Value passed from JS
if (rawValue->hasValue()) {
return true;
}
// Null/undefined passed in, indicating that we should use the default
// platform value - thereby resetting this
return false;
}
AndroidTextInputProps::AndroidTextInputProps(
const AndroidTextInputProps &sourceProps,
const RawProps &rawProps)
: ViewProps(sourceProps, rawProps),
BaseTextProps(sourceProps, rawProps),
autoCompleteType(convertRawProp(
rawProps,
"autoCompleteType",
sourceProps.autoCompleteType,
{})),
returnKeyLabel(convertRawProp(
rawProps,
"returnKeyLabel",
sourceProps.returnKeyLabel,
{})),
numberOfLines(convertRawProp(
rawProps,
"numberOfLines",
sourceProps.numberOfLines,
{0})),
disableFullscreenUI(convertRawProp(
rawProps,
"disableFullscreenUI",
sourceProps.disableFullscreenUI,
{false})),
textBreakStrategy(convertRawProp(
rawProps,
"textBreakStrategy",
sourceProps.textBreakStrategy,
{})),
underlineColorAndroid(convertRawProp(
rawProps,
"underlineColorAndroid",
sourceProps.underlineColorAndroid,
{})),
inlineImageLeft(convertRawProp(
rawProps,
"inlineImageLeft",
sourceProps.inlineImageLeft,
{})),
inlineImagePadding(convertRawProp(
rawProps,
"inlineImagePadding",
sourceProps.inlineImagePadding,
{0})),
importantForAutofill(convertRawProp(
rawProps,
"importantForAutofill",
sourceProps.importantForAutofill,
{})),
showSoftInputOnFocus(convertRawProp(
rawProps,
"showSoftInputOnFocus",
sourceProps.showSoftInputOnFocus,
{false})),
autoCapitalize(convertRawProp(
rawProps,
"autoCapitalize",
sourceProps.autoCapitalize,
{})),
autoCorrect(convertRawProp(
rawProps,
"autoCorrect",
sourceProps.autoCorrect,
{false})),
autoFocus(convertRawProp(
rawProps,
"autoFocus",
sourceProps.autoFocus,
{false})),
allowFontScaling(convertRawProp(
rawProps,
"allowFontScaling",
sourceProps.allowFontScaling,
{false})),
maxFontSizeMultiplier(convertRawProp(
rawProps,
"maxFontSizeMultiplier",
sourceProps.maxFontSizeMultiplier,
{0.0})),
editable(
convertRawProp(rawProps, "editable", sourceProps.editable, {false})),
keyboardType(convertRawProp(
rawProps,
"keyboardType",
sourceProps.keyboardType,
{})),
returnKeyType(convertRawProp(
rawProps,
"returnKeyType",
sourceProps.returnKeyType,
{})),
maxLength(
convertRawProp(rawProps, "maxLength", sourceProps.maxLength, {0})),
multiline(convertRawProp(
rawProps,
"multiline",
sourceProps.multiline,
{false})),
placeholder(
convertRawProp(rawProps, "placeholder", sourceProps.placeholder, {})),
placeholderTextColor(convertRawProp(
rawProps,
"placeholderTextColor",
sourceProps.placeholderTextColor,
{})),
secureTextEntry(convertRawProp(
rawProps,
"secureTextEntry",
sourceProps.secureTextEntry,
{false})),
selectionColor(convertRawProp(
rawProps,
"selectionColor",
sourceProps.selectionColor,
{})),
selection(
convertRawProp(rawProps, "selection", sourceProps.selection, {})),
value(convertRawProp(rawProps, "value", sourceProps.value, {})),
defaultValue(convertRawProp(
rawProps,
"defaultValue",
sourceProps.defaultValue,
{})),
selectTextOnFocus(convertRawProp(
rawProps,
"selectTextOnFocus",
sourceProps.selectTextOnFocus,
{false})),
blurOnSubmit(convertRawProp(
rawProps,
"blurOnSubmit",
sourceProps.blurOnSubmit,
{false})),
caretHidden(convertRawProp(
rawProps,
"caretHidden",
sourceProps.caretHidden,
{false})),
contextMenuHidden(convertRawProp(
rawProps,
"contextMenuHidden",
sourceProps.contextMenuHidden,
{false})),
textShadowColor(convertRawProp(
rawProps,
"textShadowColor",
sourceProps.textShadowColor,
{})),
textShadowRadius(convertRawProp(
rawProps,
"textShadowRadius",
sourceProps.textShadowRadius,
{0.0})),
textDecorationLine(convertRawProp(
rawProps,
"textDecorationLine",
sourceProps.textDecorationLine,
{})),
fontStyle(
convertRawProp(rawProps, "fontStyle", sourceProps.fontStyle, {})),
textShadowOffset(convertRawProp(
rawProps,
"textShadowOffset",
sourceProps.textShadowOffset,
{})),
lineHeight(convertRawProp(
rawProps,
"lineHeight",
sourceProps.lineHeight,
{0.0})),
textTransform(convertRawProp(
rawProps,
"textTransform",
sourceProps.textTransform,
{})),
color(convertRawProp(rawProps, "color", sourceProps.color, {0})),
letterSpacing(convertRawProp(
rawProps,
"letterSpacing",
sourceProps.letterSpacing,
{0.0})),
fontSize(
convertRawProp(rawProps, "fontSize", sourceProps.fontSize, {0.0})),
textAlign(
convertRawProp(rawProps, "textAlign", sourceProps.textAlign, {})),
includeFontPadding(convertRawProp(
rawProps,
"includeFontPadding",
sourceProps.includeFontPadding,
{false})),
fontWeight(
convertRawProp(rawProps, "fontWeight", sourceProps.fontWeight, {})),
fontFamily(
convertRawProp(rawProps, "fontFamily", sourceProps.fontFamily, {})),
textAlignVertical(convertRawProp(
rawProps,
"textAlignVertical",
sourceProps.textAlignVertical,
{})),
cursorColor(
convertRawProp(rawProps, "cursorColor", sourceProps.cursorColor, {})),
mostRecentEventCount(convertRawProp(
rawProps,
"mostRecentEventCount",
sourceProps.mostRecentEventCount,
{0})),
text(convertRawProp(rawProps, "text", sourceProps.text, {})),
paragraphAttributes(
convertRawProp(rawProps, sourceProps.paragraphAttributes, {})),
// See AndroidTextInputComponentDescriptor for usage
// TODO T63008435: can these, and this feature, be removed entirely?
hasPadding(hasValue(rawProps, sourceProps.hasPadding, "", "padding", "")),
hasPaddingHorizontal(hasValue(
rawProps,
sourceProps.hasPaddingHorizontal,
"Horizontal",
"padding",
"")),
hasPaddingVertical(hasValue(
rawProps,
sourceProps.hasPaddingVertical,
"Vertical",
"padding",
"")),
hasPaddingLeft(hasValue(
rawProps,
sourceProps.hasPaddingLeft,
"Left",
"padding",
"")),
hasPaddingTop(
hasValue(rawProps, sourceProps.hasPaddingTop, "Top", "padding", "")),
hasPaddingRight(hasValue(
rawProps,
sourceProps.hasPaddingRight,
"Right",
"padding",
"")),
hasPaddingBottom(hasValue(
rawProps,
sourceProps.hasPaddingBottom,
"Bottom",
"padding",
"")),
hasPaddingStart(hasValue(
rawProps,
sourceProps.hasPaddingStart,
"Start",
"padding",
"")),
hasPaddingEnd(
hasValue(rawProps, sourceProps.hasPaddingEnd, "End", "padding", "")) {
}
// TODO T53300085: support this in codegen; this was hand-written
folly::dynamic AndroidTextInputProps::getDynamic() const {
folly::dynamic props = folly::dynamic::object();
props["autoCompleteType"] = autoCompleteType;
props["returnKeyLabel"] = returnKeyLabel;
props["numberOfLines"] = numberOfLines;
props["disableFullscreenUI"] = disableFullscreenUI;
props["textBreakStrategy"] = textBreakStrategy;
props["underlineColorAndroid"] = toDynamic(underlineColorAndroid);
props["inlineImageLeft"] = inlineImageLeft;
props["inlineImagePadding"] = inlineImagePadding;
props["importantForAutofill"] = importantForAutofill;
props["showSoftInputOnFocus"] = showSoftInputOnFocus;
props["autoCapitalize"] = autoCapitalize;
props["autoCorrect"] = autoCorrect;
props["autoFocus"] = autoFocus;
props["allowFontScaling"] = allowFontScaling;
props["maxFontSizeMultiplier"] = maxFontSizeMultiplier;
props["editable"] = editable;
props["keyboardType"] = keyboardType;
props["returnKeyType"] = returnKeyType;
props["maxLength"] = maxLength;
props["multiline"] = multiline;
props["placeholder"] = placeholder;
props["placeholderTextColor"] = toDynamic(placeholderTextColor);
props["secureTextEntry"] = secureTextEntry;
props["selectionColor"] = toDynamic(selectionColor);
props["selection"] = toDynamic(selection);
props["value"] = value;
props["defaultValue"] = defaultValue;
props["selectTextOnFocus"] = selectTextOnFocus;
props["blurOnSubmit"] = blurOnSubmit;
props["caretHidden"] = caretHidden;
props["contextMenuHidden"] = contextMenuHidden;
props["textShadowColor"] = toDynamic(textShadowColor);
props["textShadowRadius"] = textShadowRadius;
props["textDecorationLine"] = textDecorationLine;
props["fontStyle"] = fontStyle;
props["textShadowOffset"] = toDynamic(textShadowOffset);
props["lineHeight"] = lineHeight;
props["textTransform"] = textTransform;
props["color"] = color;
props["letterSpacing"] = letterSpacing;
props["fontSize"] = fontSize;
props["textAlign"] = textAlign;
props["includeFontPadding"] = includeFontPadding;
props["fontWeight"] = fontWeight;
props["fontFamily"] = fontFamily;
props["textAlignVertical"] = textAlignVertical;
props["cursorColor"] = toDynamic(cursorColor);
props["mostRecentEventCount"] = mostRecentEventCount;
props["text"] = text;
props["hasPadding"] = hasPadding;
props["hasPaddingHorizontal"] = hasPaddingHorizontal;
props["hasPaddingVertical"] = hasPaddingVertical;
props["hasPaddingStart"] = hasPaddingStart;
props["hasPaddingEnd"] = hasPaddingEnd;
props["hasPaddingLeft"] = hasPaddingLeft;
props["hasPaddingRight"] = hasPaddingRight;
props["hasPaddingTop"] = hasPaddingTop;
props["hasPaddingBottom"] = hasPaddingBottom;
return props;
}
#pragma mark - DebugStringConvertible
#if RN_DEBUG_STRING_CONVERTIBLE
// TODO: codegen these
SharedDebugStringConvertibleList AndroidTextInputProps::getDebugProps() const {
return {};
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,183 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/components/text/BaseTextProps.h>
#include <react/core/Props.h>
#include <react/graphics/Color.h>
#include <react/graphics/Geometry.h>
#include <react/attributedstring/TextAttributes.h>
#include <react/attributedstring/conversions.h>
#include <react/components/text/BaseTextProps.h>
#include <react/components/view/ViewProps.h>
#include <react/core/propsConversions.h>
#include <react/graphics/Color.h>
#include <react/imagemanager/primitives.h>
#include <cinttypes>
#include <vector>
namespace facebook {
namespace react {
struct AndroidTextInputSelectionStruct {
int start;
int end;
};
static inline void fromRawValue(
const RawValue &value,
AndroidTextInputSelectionStruct &result) {
auto map = (better::map<std::string, RawValue>)value;
auto start = map.find("start");
if (start != map.end()) {
fromRawValue(start->second, result.start);
}
auto end = map.find("end");
if (end != map.end()) {
fromRawValue(end->second, result.end);
}
}
static inline std::string toString(
const AndroidTextInputSelectionStruct &value) {
return "[Object AndroidTextInputSelectionStruct]";
}
struct AndroidTextInputTextShadowOffsetStruct {
double width;
double height;
};
static inline void fromRawValue(
const RawValue &value,
AndroidTextInputTextShadowOffsetStruct &result) {
auto map = (better::map<std::string, RawValue>)value;
auto width = map.find("width");
if (width != map.end()) {
fromRawValue(width->second, result.width);
}
auto height = map.find("height");
if (height != map.end()) {
fromRawValue(height->second, result.height);
}
}
static inline std::string toString(
const AndroidTextInputTextShadowOffsetStruct &value) {
return "[Object AndroidTextInputTextShadowOffsetStruct]";
}
#ifdef ANDROID
inline folly::dynamic toDynamic(
const AndroidTextInputTextShadowOffsetStruct &value) {
folly::dynamic dynamicValue = folly::dynamic::object();
dynamicValue["width"] = value.width;
dynamicValue["height"] = value.height;
return dynamicValue;
}
inline folly::dynamic toDynamic(const AndroidTextInputSelectionStruct &value) {
folly::dynamic dynamicValue = folly::dynamic::object();
dynamicValue["start"] = value.start;
dynamicValue["end"] = value.end;
return dynamicValue;
}
#endif
class AndroidTextInputProps final : public ViewProps, public BaseTextProps {
public:
AndroidTextInputProps() = default;
AndroidTextInputProps(
const AndroidTextInputProps &sourceProps,
const RawProps &rawProps);
folly::dynamic getDynamic() const;
#pragma mark - Props
const std::string autoCompleteType{};
const std::string returnKeyLabel{};
const int numberOfLines{0};
const bool disableFullscreenUI{false};
const std::string textBreakStrategy{};
const SharedColor underlineColorAndroid{};
const std::string inlineImageLeft{};
const int inlineImagePadding{0};
const std::string importantForAutofill{};
const bool showSoftInputOnFocus{false};
const std::string autoCapitalize{};
const bool autoCorrect{false};
const bool autoFocus{false};
const bool allowFontScaling{false};
const Float maxFontSizeMultiplier{0.0};
const bool editable{false};
const std::string keyboardType{};
const std::string returnKeyType{};
const int maxLength{0};
const bool multiline{false};
const std::string placeholder{};
const SharedColor placeholderTextColor{};
const bool secureTextEntry{false};
const SharedColor selectionColor{};
const AndroidTextInputSelectionStruct selection{};
const std::string value{};
const std::string defaultValue{};
const bool selectTextOnFocus{false};
const bool blurOnSubmit{false};
const bool caretHidden{false};
const bool contextMenuHidden{false};
const SharedColor textShadowColor{};
const Float textShadowRadius{0.0};
const std::string textDecorationLine{};
const std::string fontStyle{};
const AndroidTextInputTextShadowOffsetStruct textShadowOffset{};
const Float lineHeight{0.0};
const std::string textTransform{};
const int color{0};
const Float letterSpacing{0.0};
const Float fontSize{0.0};
const std::string textAlign{};
const bool includeFontPadding{false};
const std::string fontWeight{};
const std::string fontFamily{};
const std::string textAlignVertical{};
const SharedColor cursorColor{};
const int mostRecentEventCount{0};
const std::string text{};
/*
* Contains all prop values that affect visual representation of the
* paragraph.
*/
ParagraphAttributes const paragraphAttributes{};
/**
* Auxiliary information to detect if these props are set or not.
* See AndroidTextInputComponentDescriptor for usage.
* TODO T63008435: can these, and this feature, be removed entirely?
*/
const bool hasPadding{};
const bool hasPaddingHorizontal{};
const bool hasPaddingVertical{};
const bool hasPaddingLeft{};
const bool hasPaddingTop{};
const bool hasPaddingRight{};
const bool hasPaddingBottom{};
const bool hasPaddingStart{};
const bool hasPaddingEnd{};
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const;
#endif
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,197 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AndroidTextInputShadowNode.h"
#include <fbjni/fbjni.h>
#include <react/attributedstring/AttributedStringBox.h>
#include <react/attributedstring/TextAttributes.h>
#include <react/components/text/BaseTextShadowNode.h>
#include <react/core/LayoutConstraints.h>
#include <react/core/LayoutContext.h>
#include <react/core/conversions.h>
#include <react/jni/ReadableNativeMap.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
extern const char AndroidTextInputComponentName[] = "AndroidTextInput";
void AndroidTextInputShadowNode::setContextContainer(
ContextContainer *contextContainer) {
ensureUnsealed();
contextContainer_ = contextContainer;
}
AttributedString AndroidTextInputShadowNode::getAttributedString() const {
// Use BaseTextShadowNode to get attributed string from children
auto childTextAttributes = TextAttributes::defaultTextAttributes();
childTextAttributes.apply(getConcreteProps().textAttributes);
auto attributedString = AttributedString{};
auto attachments = BaseTextShadowNode::Attachments{};
BaseTextShadowNode::buildAttributedString(
childTextAttributes, *this, attributedString, attachments);
// BaseTextShadowNode only gets children. We must detect and prepend text
// value attributes manually.
if (!getConcreteProps().text.empty()) {
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.apply(getConcreteProps().textAttributes);
auto fragment = AttributedString::Fragment{};
fragment.string = getConcreteProps().text;
fragment.textAttributes = textAttributes;
// If the TextInput opacity is 0 < n < 1, the opacity of the TextInput and
// text value's background will stack. This is a hack/workaround to prevent
// that effect.
fragment.textAttributes.backgroundColor = clearColor();
fragment.parentShadowView = ShadowView(*this);
attributedString.prependFragment(fragment);
}
return attributedString;
}
// For measurement purposes, we want to make sure that there's at least a
// single character in the string so that the measured height is greater
// than zero. Otherwise, empty TextInputs with no placeholder don't
// display at all.
AttributedString AndroidTextInputShadowNode::getPlaceholderAttributedString()
const {
// Return placeholder text, since text and children are empty.
auto textAttributedString = AttributedString{};
auto fragment = AttributedString::Fragment{};
fragment.string = getConcreteProps().placeholder;
if (fragment.string.empty()) {
fragment.string = " ";
}
auto textAttributes = TextAttributes::defaultTextAttributes();
textAttributes.apply(getConcreteProps().textAttributes);
// If there's no text, it's possible that this Fragment isn't actually
// appended to the AttributedString (see implementation of appendFragment)
fragment.textAttributes = textAttributes;
fragment.parentShadowView = ShadowView(*this);
textAttributedString.appendFragment(fragment);
return textAttributedString;
}
void AndroidTextInputShadowNode::setTextLayoutManager(
SharedTextLayoutManager textLayoutManager) {
ensureUnsealed();
textLayoutManager_ = textLayoutManager;
}
AttributedString AndroidTextInputShadowNode::getMostRecentAttributedString()
const {
auto const &state = getStateData();
auto reactTreeAttributedString = getAttributedString();
// Sometimes the treeAttributedString will only differ from the state
// not by inherent properties (string or prop attributes), but by the frame of
// the parent which has changed Thus, we can't directly compare the entire
// AttributedString
bool treeAttributedStringChanged =
!state.reactTreeAttributedString.compareTextAttributesWithoutFrame(
reactTreeAttributedString);
return (
!treeAttributedStringChanged ? state.attributedString
: reactTreeAttributedString);
}
void AndroidTextInputShadowNode::updateStateIfNeeded() {
ensureUnsealed();
auto reactTreeAttributedString = getAttributedString();
auto const &state = getStateData();
assert(textLayoutManager_);
assert(
(!state.layoutManager || state.layoutManager == textLayoutManager_) &&
"`StateData` refers to a different `TextLayoutManager`");
// Tree is often out of sync with the value of the TextInput.
// This is by design - don't change the value of the TextInput in the State,
// and therefore in Java, unless the tree itself changes.
if (state.reactTreeAttributedString == reactTreeAttributedString &&
state.layoutManager == textLayoutManager_) {
return;
}
// Store default TextAttributes in state.
// In the case where the TextInput is completely empty (no value, no
// defaultValue, no placeholder, no children) there are therefore no fragments
// in the AttributedString, and when State is updated, it needs some way to
// reconstruct a Fragment with default TextAttributes.
auto defaultTextAttributes = TextAttributes::defaultTextAttributes();
defaultTextAttributes.apply(getConcreteProps().textAttributes);
auto newEventCount =
(state.reactTreeAttributedString == reactTreeAttributedString
? 0
: getConcreteProps().mostRecentEventCount);
auto newAttributedString = getMostRecentAttributedString();
// Even if we're here and updating state, it may be only to update the layout
// manager If that is the case, make sure we don't update text: pass in the
// current attributedString unchanged, and pass in zero for the "event count"
// so no changes are applied There's no way to prevent a state update from
// flowing to Java, so we just ensure it's a noop in those cases.
setStateData(AndroidTextInputState{newEventCount,
newAttributedString,
reactTreeAttributedString,
getConcreteProps().paragraphAttributes,
defaultTextAttributes,
ShadowView(*this),
textLayoutManager_,
state.defaultThemePaddingStart,
state.defaultThemePaddingEnd,
state.defaultThemePaddingTop,
state.defaultThemePaddingBottom});
}
#pragma mark - LayoutableShadowNode
Size AndroidTextInputShadowNode::measure(
LayoutConstraints layoutConstraints) const {
// Layout is called right after measure.
// Measure is marked as `const`, and `layout` is not; so State can be updated
// during layout, but not during `measure`. If State is out-of-date in layout,
// it's too late: measure will have already operated on old State. Thus, we
// use the same value here that we *will* use in layout to update the state.
AttributedString attributedString = getMostRecentAttributedString();
if (attributedString.isEmpty()) {
attributedString = getPlaceholderAttributedString();
}
if (attributedString.isEmpty()) {
return {0, 0};
}
return textLayoutManager_
->measure(
AttributedStringBox{attributedString},
getConcreteProps().paragraphAttributes,
layoutConstraints)
.size;
}
void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) {
updateStateIfNeeded();
ConcreteViewShadowNode::layout(layoutContext);
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) Facebook, Inc. and its 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 "AndroidTextInputEventEmitter.h"
#include "AndroidTextInputProps.h"
#include "AndroidTextInputState.h"
#include <react/components/view/ConcreteViewShadowNode.h>
#include <react/utils/ContextContainer.h>
#include <react/attributedstring/AttributedString.h>
namespace facebook {
namespace react {
extern const char AndroidTextInputComponentName[];
/*
* `ShadowNode` for <AndroidTextInput> component.
*/
class AndroidTextInputShadowNode : public ConcreteViewShadowNode<
AndroidTextInputComponentName,
AndroidTextInputProps,
AndroidTextInputEventEmitter,
AndroidTextInputState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
void setContextContainer(ContextContainer *contextContainer);
/*
* Returns a `AttributedString` which represents text content of the node.
*/
AttributedString getAttributedString() const;
AttributedString getPlaceholderAttributedString() const;
/*
* Associates a shared TextLayoutManager with the node.
* `ParagraphShadowNode` uses the manager to measure text content
* and construct `ParagraphState` objects.
*/
void setTextLayoutManager(SharedTextLayoutManager textLayoutManager);
#pragma mark - LayoutableShadowNode
Size measure(LayoutConstraints layoutConstraints) const override;
void layout(LayoutContext layoutContext) override;
private:
ContextContainer *contextContainer_{};
/**
* Get the most up-to-date attributed string for measurement and State.
*/
AttributedString getMostRecentAttributedString() const;
/*
* Creates a `State` object (with `AttributedText` and
* `TextLayoutManager`) if needed.
*/
void updateStateIfNeeded();
SharedTextLayoutManager textLayoutManager_;
/*
* Cached attributed string that represents the content of the subtree started
* from the node.
*/
mutable folly::Optional<AttributedString> cachedAttributedString_{};
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "AndroidTextInputState.h"
#include <react/components/text/conversions.h>
#include <react/debug/debugStringConvertibleUtils.h>
namespace facebook {
namespace react {
#ifdef ANDROID
folly::dynamic AndroidTextInputState::getDynamic() const {
// Java doesn't need all fields, so we don't pass them along.
folly::dynamic newState = folly::dynamic::object();
newState["mostRecentEventCount"] = mostRecentEventCount;
newState["attributedString"] = toDynamic(attributedString);
newState["paragraphAttributes"] = toDynamic(paragraphAttributes);
newState["hash"] = newState["attributedString"]["hash"];
newState["hasThemeData"] = !isnan(defaultThemePaddingStart);
return newState;
}
#endif
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,185 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <react/attributedstring/AttributedString.h>
#include <react/attributedstring/ParagraphAttributes.h>
#include <react/textlayoutmanager/TextLayoutManager.h>
#ifdef ANDROID
#include <folly/dynamic.h>
#endif
namespace facebook {
namespace react {
/*
* State for <Paragraph> component.
* Represents what to render and how to render.
*/
class AndroidTextInputState final {
public:
int64_t mostRecentEventCount{0};
/*
* All content of <TextInput> component represented as an `AttributedString`.
*/
AttributedString attributedString{};
/*
* All content of <TextInput> component represented as an `AttributedString`.
* This stores the previous computed *from the React tree*. This usually
* doesn't change as the TextInput contents are being updated. If it does
* change, we need to wipe out current contents of the TextInput and replace
* with the new value from the tree.
*/
AttributedString reactTreeAttributedString{};
/*
* Represents all visual attributes of a paragraph of text represented as
* a ParagraphAttributes.
*/
ParagraphAttributes paragraphAttributes{};
/**
* Default TextAttributes used if we need to construct a new Fragment.
* Only used if text is inserted into an AttributedString with no existing
* Fragments.
*/
TextAttributes defaultTextAttributes;
/**
* Default parent ShadowView used if we need to construct a new Fragment.
* Only used if text is inserted into an AttributedString with no existing
* Fragments.
*/
ShadowView defaultParentShadowView;
/*
* `TextLayoutManager` provides a connection to platform-specific
* text rendering infrastructure which is capable to render the
* `AttributedString`.
*/
SharedTextLayoutManager layoutManager{};
/**
* Communicates Android theme padding back to the ShadowNode / Component
* Descriptor for layout.
*/
float defaultThemePaddingStart{NAN};
float defaultThemePaddingEnd{NAN};
float defaultThemePaddingTop{NAN};
float defaultThemePaddingBottom{NAN};
#ifdef ANDROID
AttributedString updateAttributedString(
TextAttributes const &defaultTextAttributes,
ShadowView const &defaultParentShadowView,
AttributedString const &original,
folly::dynamic const &data) {
if (data["textChanged"].empty()) {
return original;
}
// TODO: parse other attributes besides just string?
// on the other hand, not much should be driven from Java
// TODO: it'd be really nice to treat these as operational transforms
// instead of having to pass the whole string across.
// Unfortunately we don't have a good way of communicating from Java to C++
// *which* version of the State changes should be applied to; and if there's
// a conflict, we don't have any recourse of any way to bail out of a
// commit.
auto str = AttributedString{};
int i = 0;
folly::dynamic fragments = data["textChanged"]["fragments"];
for (auto const &fragment : original.getFragments()) {
str.appendFragment(AttributedString::Fragment{
fragments.size() > i ? fragments[i]["string"].getString() : "",
fragment.textAttributes,
fragment.parentShadowView});
i++;
}
if (fragments.size() > original.getFragments().size()) {
for (; i < fragments.size(); i++) {
str.appendFragment(
AttributedString::Fragment{fragments[i]["string"].getString(),
defaultTextAttributes,
defaultParentShadowView});
}
}
return str;
}
AndroidTextInputState(
int64_t mostRecentEventCount,
AttributedString const &attributedString,
AttributedString const &reactTreeAttributedString,
ParagraphAttributes const &paragraphAttributes,
TextAttributes const &defaultTextAttributes,
ShadowView const &defaultParentShadowView,
SharedTextLayoutManager const &layoutManager,
float defaultThemePaddingStart,
float defaultThemePaddingEnd,
float defaultThemePaddingTop,
float defaultThemePaddingBottom)
: mostRecentEventCount(mostRecentEventCount),
attributedString(attributedString),
reactTreeAttributedString(reactTreeAttributedString),
paragraphAttributes(paragraphAttributes),
defaultTextAttributes(defaultTextAttributes),
defaultParentShadowView(defaultParentShadowView),
layoutManager(layoutManager),
defaultThemePaddingStart(defaultThemePaddingStart),
defaultThemePaddingEnd(defaultThemePaddingEnd),
defaultThemePaddingTop(defaultThemePaddingTop),
defaultThemePaddingBottom(defaultThemePaddingBottom) {}
AndroidTextInputState() = default;
AndroidTextInputState(
AndroidTextInputState const &previousState,
folly::dynamic const &data)
: mostRecentEventCount(data.getDefault(
"mostRecentEventCount",
previousState.mostRecentEventCount)
.getInt()),
attributedString(updateAttributedString(
previousState.defaultTextAttributes,
previousState.defaultParentShadowView,
previousState.attributedString,
data)),
reactTreeAttributedString(previousState.reactTreeAttributedString),
paragraphAttributes(previousState.paragraphAttributes),
defaultTextAttributes(previousState.defaultTextAttributes),
defaultParentShadowView(previousState.defaultParentShadowView),
layoutManager(previousState.layoutManager),
defaultThemePaddingStart(data.getDefault(
"themePaddingStart",
previousState.defaultThemePaddingStart)
.getDouble()),
defaultThemePaddingEnd(data.getDefault(
"themePaddingEnd",
previousState.defaultThemePaddingEnd)
.getDouble()),
defaultThemePaddingTop(data.getDefault(
"themePaddingTop",
previousState.defaultThemePaddingTop)
.getDouble()),
defaultThemePaddingBottom(
data.getDefault(
"themePaddingBottom",
previousState.defaultThemePaddingBottom)
.getDouble()){};
folly::dynamic getDynamic() const;
#endif
};
} // namespace react
} // namespace facebook