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",