diff --git a/change/react-native-windows-c4c264df-7619-4559-b43f-9761d5d68d63.json b/change/react-native-windows-c4c264df-7619-4559-b43f-9761d5d68d63.json
new file mode 100644
index 00000000000..a8131090345
--- /dev/null
+++ b/change/react-native-windows-c4c264df-7619-4559-b43f-9761d5d68d63.json
@@ -0,0 +1,7 @@
+{
+ "type": "none",
+ "comment": "Implement button property",
+ "packageName": "react-native-windows",
+ "email": "hmalothu@microsoft.com",
+ "dependentChangeType": "none"
+}
diff --git a/packages/playground/Samples/click.tsx b/packages/playground/Samples/click.tsx
index bc1ad0feeff..f9c5cddaecd 100644
--- a/packages/playground/Samples/click.tsx
+++ b/packages/playground/Samples/click.tsx
@@ -10,6 +10,7 @@ import {AppRegistry, Text, TouchableHighlight, View} from 'react-native';
export default class Bootstrap extends React.Component {
state = {
ticker: 0,
+ lastEvent: 'Click the box to test pointer events',
};
onSmallIncrement = () => {
@@ -27,32 +28,82 @@ export default class Bootstrap extends React.Component {
console.log(' onLargeIncrement !');
};
+ buttonLabel = (button: number) => {
+ switch (button) {
+ case 0:
+ return 'Left';
+ case 1:
+ return 'Middle';
+ case 2:
+ return 'Right';
+ default:
+ return `Unknown(${button})`;
+ }
+ };
+
render() {
return (
-
-
+ {/* Pointer event test */}
+ {
+ const {button, buttons} = e.nativeEvent;
+ this.setState({
+ lastEvent: `${this.buttonLabel(
+ button,
+ )} pressed (button=${button}, buttons=${buttons})`,
+ });
+ }}
+ onPointerUp={(e: any) => {
+ const {button, buttons} = e.nativeEvent;
+ this.setState({
+ lastEvent: `${this.buttonLabel(
+ button,
+ )} released (button=${button}, buttons=${buttons})`,
+ });
+ }}
+ {...{onClick: this.onLargeIncrement}}>
+ Click Me
+
+
+ {this.state.lastEvent}
+
+
+ {/* Original click test */}
+
-
-
- {this.state.ticker.toString()}
-
-
+ style={{backgroundColor: 'orange', margin: 15}}
+ onPress={this.onMediumIncrement}
+ {...{
+ // Use weird format as work around for the fact that these props are not part of the @types/react-native yet
+ focusable: true,
+ }}>
+
+
+
+ {this.state.ticker.toString()}
+
+
+
-
+
);
}
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp
index 0ff00758bb7..01eaf359155 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp
@@ -1210,6 +1210,30 @@ void CompositionEventHandler::onPointerPressed(
ActiveTouch activeTouch{0};
activeTouch.touchType = UITouchType::Mouse;
+ // Map PointerUpdateKind to W3C button value
+ // https://developer.mozilla.org/docs/Web/API/MouseEvent/button
+ auto updateKind = pointerPoint.Properties().PointerUpdateKind();
+ switch (updateKind) {
+ case Composition::Input::PointerUpdateKind::LeftButtonPressed:
+ activeTouch.button = 0;
+ break;
+ case Composition::Input::PointerUpdateKind::MiddleButtonPressed:
+ activeTouch.button = 1;
+ break;
+ case Composition::Input::PointerUpdateKind::RightButtonPressed:
+ activeTouch.button = 2;
+ break;
+ case Composition::Input::PointerUpdateKind::XButton1Pressed:
+ activeTouch.button = 3;
+ break;
+ case Composition::Input::PointerUpdateKind::XButton2Pressed:
+ activeTouch.button = 4;
+ break;
+ default:
+ activeTouch.button = -1;
+ break;
+ }
+
while (targetComponentView) {
if (auto eventEmitter =
winrt::get_self(targetComponentView)
@@ -1394,8 +1418,34 @@ facebook::react::PointerEvent CompositionEventHandler::CreatePointerEventFromAct
event.detail = 0;
- // event.button = activeTouch.button;
- // event.buttons = ButtonMaskToButtons(activeTouch.buttonMask);
+ event.button = activeTouch.button;
+
+ // Build W3C buttons bitmask from the active button
+ // https://developer.mozilla.org/docs/Web/API/MouseEvent/buttons
+ if (IsEndishEventType(eventType)) {
+ event.buttons = 0;
+ } else {
+ switch (activeTouch.button) {
+ case 0:
+ event.buttons = 1;
+ break; // primary
+ case 1:
+ event.buttons = 4;
+ break; // auxiliary (middle)
+ case 2:
+ event.buttons = 2;
+ break; // secondary (right)
+ case 3:
+ event.buttons = 8;
+ break; // X1
+ case 4:
+ event.buttons = 16;
+ break; // X2
+ default:
+ event.buttons = 0;
+ break;
+ }
+ }
// UpdatePointerEventModifierFlags(event, activeTouch.modifierFlags);
diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/view/BaseViewProps.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/view/BaseViewProps.cpp
new file mode 100644
index 00000000000..00f9698cb7a
--- /dev/null
+++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/view/BaseViewProps.cpp
@@ -0,0 +1,664 @@
+/*
+ * 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.
+ */
+
+#include "BaseViewProps.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace facebook::react {
+
+namespace {
+
+std::array getTranslateForTransformOrigin(
+ float viewWidth,
+ float viewHeight,
+ TransformOrigin transformOrigin) {
+ float viewCenterX = viewWidth / 2;
+ float viewCenterY = viewHeight / 2;
+
+ std::array origin = {viewCenterX, viewCenterY, transformOrigin.z};
+
+ for (size_t i = 0; i < transformOrigin.xy.size(); ++i) {
+ auto& currentOrigin = transformOrigin.xy[i];
+ if (currentOrigin.unit == UnitType::Point) {
+ origin[i] = currentOrigin.value;
+ } else if (currentOrigin.unit == UnitType::Percent) {
+ origin[i] =
+ ((i == 0) ? viewWidth : viewHeight) * currentOrigin.value / 100.0f;
+ }
+ }
+
+ float newTranslateX = -viewCenterX + origin[0];
+ float newTranslateY = -viewCenterY + origin[1];
+ float newTranslateZ = origin[2];
+
+ return std::array{newTranslateX, newTranslateY, newTranslateZ};
+}
+
+} // namespace
+
+BaseViewProps::BaseViewProps(
+ const PropsParserContext& context,
+ const BaseViewProps& sourceProps,
+ const RawProps& rawProps,
+ const std::function& filterObjectKeys)
+ : YogaStylableProps(context, sourceProps, rawProps, filterObjectKeys),
+ AccessibilityProps(context, sourceProps, rawProps),
+ opacity(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.opacity
+ : convertRawProp(
+ context,
+ rawProps,
+ "opacity",
+ sourceProps.opacity,
+ (Float)1.0)),
+ backgroundColor(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.backgroundColor
+ : convertRawProp(
+ context,
+ rawProps,
+ "backgroundColor",
+ sourceProps.backgroundColor,
+ {})),
+ borderRadii(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.borderRadii
+ : convertRawProp(
+ context,
+ rawProps,
+ "border",
+ "Radius",
+ sourceProps.borderRadii,
+ {})),
+ borderColors(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.borderColors
+ : convertRawProp(
+ context,
+ rawProps,
+ "border",
+ "Color",
+ sourceProps.borderColors,
+ {})),
+ borderCurves(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.borderCurves
+ : convertRawProp(
+ context,
+ rawProps,
+ "border",
+ "Curve",
+ sourceProps.borderCurves,
+ {})),
+ borderStyles(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.borderStyles
+ : convertRawProp(
+ context,
+ rawProps,
+ "border",
+ "Style",
+ sourceProps.borderStyles,
+ {})),
+ outlineColor(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.outlineColor
+ : convertRawProp(
+ context,
+ rawProps,
+ "outlineColor",
+ sourceProps.outlineColor,
+ {})),
+ outlineOffset(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.outlineOffset
+ : convertRawProp(
+ context,
+ rawProps,
+ "outlineOffset",
+ sourceProps.outlineOffset,
+ {})),
+ outlineStyle(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.outlineStyle
+ : convertRawProp(
+ context,
+ rawProps,
+ "outlineStyle",
+ sourceProps.outlineStyle,
+ {})),
+ outlineWidth(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.outlineWidth
+ : convertRawProp(
+ context,
+ rawProps,
+ "outlineWidth",
+ sourceProps.outlineWidth,
+ {})),
+ shadowColor(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.shadowColor
+ : convertRawProp(
+ context,
+ rawProps,
+ "shadowColor",
+ sourceProps.shadowColor,
+ {})),
+ shadowOffset(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.shadowOffset
+ : convertRawProp(
+ context,
+ rawProps,
+ "shadowOffset",
+ sourceProps.shadowOffset,
+ {})),
+ shadowOpacity(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.shadowOpacity
+ : convertRawProp(
+ context,
+ rawProps,
+ "shadowOpacity",
+ sourceProps.shadowOpacity,
+ {})),
+ shadowRadius(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.shadowRadius
+ : convertRawProp(
+ context,
+ rawProps,
+ "shadowRadius",
+ sourceProps.shadowRadius,
+ {})),
+ cursor(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.cursor
+ : convertRawProp(
+ context,
+ rawProps,
+ "cursor",
+ sourceProps.cursor,
+ {})),
+ boxShadow(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.boxShadow
+ : convertRawProp(
+ context,
+ rawProps,
+ "boxShadow",
+ sourceProps.boxShadow,
+ {})),
+ filter(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.filter
+ : convertRawProp(
+ context,
+ rawProps,
+ "filter",
+ sourceProps.filter,
+ {})),
+ backgroundImage(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.backgroundImage
+ : convertRawProp(
+ context,
+ rawProps,
+ "experimental_backgroundImage",
+ sourceProps.backgroundImage,
+ {})),
+ backgroundSize(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.backgroundSize
+ : convertRawProp(
+ context,
+ rawProps,
+ "experimental_backgroundSize",
+ sourceProps.backgroundSize,
+ {})),
+ backgroundPosition(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.backgroundPosition
+ : convertRawProp(
+ context,
+ rawProps,
+ "experimental_backgroundPosition",
+ sourceProps.backgroundPosition,
+ {})),
+ backgroundRepeat(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.backgroundRepeat
+ : convertRawProp(
+ context,
+ rawProps,
+ "experimental_backgroundRepeat",
+ sourceProps.backgroundRepeat,
+ {})),
+ mixBlendMode(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.mixBlendMode
+ : convertRawProp(
+ context,
+ rawProps,
+ "mixBlendMode",
+ sourceProps.mixBlendMode,
+ {})),
+ isolation(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.isolation
+ : convertRawProp(
+ context,
+ rawProps,
+ "isolation",
+ sourceProps.isolation,
+ {})),
+ transform(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.transform
+ : convertRawProp(
+ context,
+ rawProps,
+ "transform",
+ sourceProps.transform,
+ {})),
+ transformOrigin(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.transformOrigin
+ : convertRawProp(
+ context,
+ rawProps,
+ "transformOrigin",
+ sourceProps.transformOrigin,
+ {})),
+ backfaceVisibility(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.backfaceVisibility
+ : convertRawProp(
+ context,
+ rawProps,
+ "backfaceVisibility",
+ sourceProps.backfaceVisibility,
+ {})),
+ shouldRasterize(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.shouldRasterize
+ : convertRawProp(
+ context,
+ rawProps,
+ "shouldRasterizeIOS",
+ sourceProps.shouldRasterize,
+ {})),
+ zIndex(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.zIndex
+ : convertRawProp(
+ context,
+ rawProps,
+ "zIndex",
+ sourceProps.zIndex,
+ {})),
+ pointerEvents(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.pointerEvents
+ : convertRawProp(
+ context,
+ rawProps,
+ "pointerEvents",
+ sourceProps.pointerEvents,
+ {})),
+ hitSlop(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.hitSlop
+ : convertRawProp(
+ context,
+ rawProps,
+ "hitSlop",
+ sourceProps.hitSlop,
+ {})),
+ onLayout(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.onLayout
+ : convertRawProp(
+ context,
+ rawProps,
+ "onLayout",
+ sourceProps.onLayout,
+ {})),
+ events(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.events
+ : convertRawProp(context, rawProps, sourceProps.events, {})),
+ collapsable(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.collapsable
+ : convertRawProp(
+ context,
+ rawProps,
+ "collapsable",
+ sourceProps.collapsable,
+ true)),
+ collapsableChildren(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.collapsableChildren
+ : convertRawProp(
+ context,
+ rawProps,
+ "collapsableChildren",
+ sourceProps.collapsableChildren,
+ true)),
+ removeClippedSubviews(
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
+ ? sourceProps.removeClippedSubviews
+ : convertRawProp(
+ context,
+ rawProps,
+ "removeClippedSubviews",
+ sourceProps.removeClippedSubviews,
+ false)) {}
+
+#define VIEW_EVENT_CASE(eventType) \
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("on" #eventType): { \
+ const auto offset = ViewEvents::Offset::eventType; \
+ ViewEvents defaultViewEvents{}; \
+ bool res = defaultViewEvents[offset]; \
+ if (value.hasValue()) { \
+ fromRawValue(context, value, res); \
+ } \
+ events[offset] = res; \
+ return; \
+ }
+
+void BaseViewProps::setProp(
+ const PropsParserContext& context,
+ RawPropsPropNameHash hash,
+ const char* propName,
+ const RawValue& value) {
+ // All Props structs setProp methods must always, unconditionally,
+ // call all super::setProp methods, since multiple structs may
+ // reuse the same values.
+ YogaStylableProps::setProp(context, hash, propName, value);
+ AccessibilityProps::setProp(context, hash, propName, value);
+
+ static auto defaults = BaseViewProps{};
+
+ switch (hash) {
+ RAW_SET_PROP_SWITCH_CASE_BASIC(opacity);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(backgroundColor);
+ RAW_SET_PROP_SWITCH_CASE(backgroundImage, "experimental_backgroundImage");
+ RAW_SET_PROP_SWITCH_CASE(backgroundSize, "experimental_backgroundSize");
+ RAW_SET_PROP_SWITCH_CASE(
+ backgroundPosition, "experimental_backgroundPosition");
+ RAW_SET_PROP_SWITCH_CASE(backgroundRepeat, "experimental_backgroundRepeat");
+ RAW_SET_PROP_SWITCH_CASE_BASIC(shadowColor);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(shadowOffset);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(shadowOpacity);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(shadowRadius);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(transform);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(backfaceVisibility);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(shouldRasterize);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(zIndex);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(pointerEvents);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(isolation);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(hitSlop);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(onLayout);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(collapsable);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(collapsableChildren);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(removeClippedSubviews);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(cursor);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(outlineColor);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(outlineOffset);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(outlineStyle);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(outlineWidth);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(filter);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(boxShadow);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(mixBlendMode);
+ // events field
+ VIEW_EVENT_CASE(PointerEnter);
+ VIEW_EVENT_CASE(PointerEnterCapture);
+ VIEW_EVENT_CASE(PointerMove);
+ VIEW_EVENT_CASE(PointerMoveCapture);
+ VIEW_EVENT_CASE(PointerLeave);
+ VIEW_EVENT_CASE(PointerLeaveCapture);
+ VIEW_EVENT_CASE(PointerOver);
+ VIEW_EVENT_CASE(PointerOverCapture);
+ VIEW_EVENT_CASE(PointerOut);
+ VIEW_EVENT_CASE(PointerOutCapture);
+ // [Windows
+ VIEW_EVENT_CASE(Click);
+ VIEW_EVENT_CASE(ClickCapture);
+ VIEW_EVENT_CASE(PointerDown);
+ VIEW_EVENT_CASE(PointerDownCapture);
+ VIEW_EVENT_CASE(PointerUp);
+ VIEW_EVENT_CASE(PointerUpCapture);
+ VIEW_EVENT_CASE(GotPointerCapture);
+ VIEW_EVENT_CASE(LostPointerCapture);
+ // Windows]
+ VIEW_EVENT_CASE(MoveShouldSetResponder);
+ VIEW_EVENT_CASE(MoveShouldSetResponderCapture);
+ VIEW_EVENT_CASE(StartShouldSetResponder);
+ VIEW_EVENT_CASE(StartShouldSetResponderCapture);
+ VIEW_EVENT_CASE(ResponderGrant);
+ VIEW_EVENT_CASE(ResponderReject);
+ VIEW_EVENT_CASE(ResponderStart);
+ VIEW_EVENT_CASE(ResponderEnd);
+ VIEW_EVENT_CASE(ResponderRelease);
+ VIEW_EVENT_CASE(ResponderMove);
+ VIEW_EVENT_CASE(ResponderTerminate);
+ VIEW_EVENT_CASE(ResponderTerminationRequest);
+ VIEW_EVENT_CASE(ShouldBlockNativeResponder);
+ VIEW_EVENT_CASE(TouchStart);
+ VIEW_EVENT_CASE(TouchMove);
+ VIEW_EVENT_CASE(TouchEnd);
+ VIEW_EVENT_CASE(TouchCancel);
+ // BorderRadii
+ SET_CASCADED_RECTANGLE_CORNERS(borderRadii, "border", "Radius", value);
+ SET_CASCADED_RECTANGLE_EDGES(borderColors, "border", "Color", value);
+ SET_CASCADED_RECTANGLE_EDGES(borderStyles, "border", "Style", value);
+ }
+}
+
+#pragma mark - Convenience Methods
+
+static BorderRadii ensureNoOverlap(const BorderRadii& radii, const Size& size) {
+ // "Corner curves must not overlap: When the sum of any two adjacent border
+ // radii exceeds the size of the border box, UAs must proportionally reduce
+ // the used values of all border radii until none of them overlap."
+ // Source: https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
+
+ float leftEdgeRadii = radii.topLeft.vertical + radii.bottomLeft.vertical;
+ float topEdgeRadii = radii.topLeft.horizontal + radii.topRight.horizontal;
+ float rightEdgeRadii = radii.topRight.vertical + radii.bottomRight.vertical;
+ float bottomEdgeRadii =
+ radii.bottomLeft.horizontal + radii.bottomRight.horizontal;
+
+ float leftEdgeRadiiScale =
+ (leftEdgeRadii > 0) ? std::min(size.height / leftEdgeRadii, (Float)1) : 0;
+ float topEdgeRadiiScale =
+ (topEdgeRadii > 0) ? std::min(size.width / topEdgeRadii, (Float)1) : 0;
+ float rightEdgeRadiiScale = (rightEdgeRadii > 0)
+ ? std::min(size.height / rightEdgeRadii, (Float)1)
+ : 0;
+ float bottomEdgeRadiiScale = (bottomEdgeRadii > 0)
+ ? std::min(size.width / bottomEdgeRadii, (Float)1)
+ : 0;
+
+ return BorderRadii{
+ .topLeft =
+ {static_cast(
+ radii.topLeft.vertical *
+ std::min(topEdgeRadiiScale, leftEdgeRadiiScale)),
+ static_cast(
+ radii.topLeft.horizontal *
+ std::min(topEdgeRadiiScale, leftEdgeRadiiScale))},
+ .topRight =
+ {static_cast(
+ radii.topRight.vertical *
+ std::min(topEdgeRadiiScale, rightEdgeRadiiScale)),
+ static_cast(
+ radii.topRight.horizontal *
+ std::min(topEdgeRadiiScale, rightEdgeRadiiScale))},
+ .bottomLeft =
+ {static_cast(
+ radii.bottomLeft.vertical *
+ std::min(bottomEdgeRadiiScale, leftEdgeRadiiScale)),
+ static_cast(
+ radii.bottomLeft.horizontal *
+ std::min(bottomEdgeRadiiScale, leftEdgeRadiiScale))},
+ .bottomRight =
+ {static_cast(
+ radii.bottomRight.vertical *
+ std::min(bottomEdgeRadiiScale, rightEdgeRadiiScale)),
+ static_cast(
+ radii.bottomRight.horizontal *
+ std::min(bottomEdgeRadiiScale, rightEdgeRadiiScale))},
+ };
+}
+
+static BorderRadii radiiPercentToPoint(
+ const RectangleCorners& radii,
+ const Size& size) {
+ return BorderRadii{
+ .topLeft =
+ {radii.topLeft.resolve(size.height),
+ radii.topLeft.resolve(size.width)},
+ .topRight =
+ {radii.topRight.resolve(size.height),
+ radii.topRight.resolve(size.width)},
+ .bottomLeft =
+ {radii.bottomLeft.resolve(size.height),
+ radii.bottomLeft.resolve(size.width)},
+ .bottomRight =
+ {radii.bottomRight.resolve(size.height),
+ radii.bottomRight.resolve(size.width)},
+ };
+}
+
+CascadedBorderWidths BaseViewProps::getBorderWidths() const {
+ return CascadedBorderWidths{
+ .left = optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::Left)),
+ .top = optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::Top)),
+ .right = optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::Right)),
+ .bottom =
+ optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::Bottom)),
+ .start = optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::Start)),
+ .end = optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::End)),
+ .horizontal =
+ optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::Horizontal)),
+ .vertical =
+ optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::Vertical)),
+ .all = optionalFloatFromYogaValue(yogaStyle.border(yoga::Edge::All)),
+ };
+}
+
+BorderMetrics BaseViewProps::resolveBorderMetrics(
+ const LayoutMetrics& layoutMetrics) const {
+ auto isRTL =
+ bool{layoutMetrics.layoutDirection == LayoutDirection::RightToLeft};
+
+ auto borderWidths = getBorderWidths();
+
+ BorderRadii radii = radiiPercentToPoint(
+ borderRadii.resolve(isRTL, ValueUnit{0.0f, UnitType::Point}),
+ layoutMetrics.frame.size);
+
+ return {
+ .borderColors = borderColors.resolve(isRTL, {}),
+ .borderWidths = borderWidths.resolve(isRTL, 0),
+ .borderRadii = ensureNoOverlap(radii, layoutMetrics.frame.size),
+ .borderCurves = borderCurves.resolve(isRTL, BorderCurve::Circular),
+ .borderStyles = borderStyles.resolve(isRTL, BorderStyle::Solid),
+ };
+}
+
+Transform BaseViewProps::resolveTransform(
+ const LayoutMetrics& layoutMetrics) const {
+ const auto& frameSize = layoutMetrics.frame.size;
+ return resolveTransform(frameSize, transform, transformOrigin);
+}
+
+Transform BaseViewProps::resolveTransform(
+ const Size& frameSize,
+ const Transform& transform,
+ const TransformOrigin& transformOrigin) {
+ auto transformMatrix = Transform{};
+
+ // transform is matrix
+ if (transform.operations.size() == 1 &&
+ transform.operations[0].type == TransformOperationType::Arbitrary) {
+ transformMatrix = transform;
+ } else {
+ for (const auto& operation : transform.operations) {
+ transformMatrix = transformMatrix *
+ Transform::FromTransformOperation(operation, frameSize, transform);
+ }
+ }
+
+ if (transformOrigin.isSet()) {
+ std::array translateOffsets = getTranslateForTransformOrigin(
+ frameSize.width, frameSize.height, transformOrigin);
+ transformMatrix =
+ Transform::Translate(
+ translateOffsets[0], translateOffsets[1], translateOffsets[2]) *
+ transformMatrix *
+ Transform::Translate(
+ -translateOffsets[0], -translateOffsets[1], -translateOffsets[2]);
+ }
+
+ return transformMatrix;
+}
+
+bool BaseViewProps::getClipsContentToBounds() const {
+ return yogaStyle.overflow() != yoga::Overflow::Visible;
+}
+
+#pragma mark - DebugStringConvertible
+
+#if RN_DEBUG_STRING_CONVERTIBLE
+SharedDebugStringConvertibleList BaseViewProps::getDebugProps() const {
+ const auto& defaultBaseViewProps = BaseViewProps();
+
+ return AccessibilityProps::getDebugProps() +
+ YogaStylableProps::getDebugProps() +
+ SharedDebugStringConvertibleList{
+ debugStringConvertibleItem(
+ "opacity", opacity, defaultBaseViewProps.opacity),
+ debugStringConvertibleItem(
+ "backgroundColor",
+ backgroundColor,
+ defaultBaseViewProps.backgroundColor),
+ debugStringConvertibleItem(
+ "zIndex", zIndex, defaultBaseViewProps.zIndex.value_or(0)),
+ debugStringConvertibleItem(
+ "pointerEvents",
+ pointerEvents,
+ defaultBaseViewProps.pointerEvents),
+ debugStringConvertibleItem(
+ "transform", transform, defaultBaseViewProps.transform),
+ debugStringConvertibleItem(
+ "backgroundImage",
+ backgroundImage,
+ defaultBaseViewProps.backgroundImage),
+ };
+}
+#endif
+
+} // namespace facebook::react
diff --git a/vnext/overrides.json b/vnext/overrides.json
index 3a54c646bd2..9642b5cb3d1 100644
--- a/vnext/overrides.json
+++ b/vnext/overrides.json
@@ -259,6 +259,13 @@
"baseFile": "packages/react-native/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h",
"baseHash": "72cc422293c0e70f454225476cdabd3458bbce5e"
},
+ {
+ "type": "patch",
+ "file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/view/BaseViewProps.cpp",
+ "baseFile": "packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp",
+ "baseHash": "24baef3c27668197629949d63f1170f2a43e2c15",
+ "issue": 15827
+ },
{
"type": "patch",
"file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/core/EventDispatcher.cpp",