From 25cba9b00e43d44a43b89246dcaadd538d34a696 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 24 Jul 2025 08:37:22 +0200 Subject: [PATCH 01/12] Init button wrapper shadow node --- .../react-native-gesture-handler/package.json | 3 +- .../ComponentDescriptors.h | 2 + ...eHandlerButtonWrapperComponentDescriptor.h | 32 ++++++++++ ...NGestureHandlerButtonWrapperShadowNode.cpp | 59 +++++++++++++++++++ .../RNGestureHandlerButtonWrapperShadowNode.h | 57 ++++++++++++++++++ .../RNGestureHandlerButtonWrapperState.h | 32 ++++++++++ ...tureHandlerButtonWrapperNativeComponent.ts | 11 ++++ 7 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperComponentDescriptor.h create mode 100644 packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp create mode 100644 packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h create mode 100644 packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperState.h create mode 100644 packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonWrapperNativeComponent.ts diff --git a/packages/react-native-gesture-handler/package.json b/packages/react-native-gesture-handler/package.json index 4af1062b40..8d2d06649f 100644 --- a/packages/react-native-gesture-handler/package.json +++ b/packages/react-native-gesture-handler/package.json @@ -129,7 +129,8 @@ "ios": { "componentProvider": { "RNGestureHandlerButton": "RNGestureHandlerButtonComponentView", - "RNGestureHandlerDetector": "RNGestureHandlerDetector" + "RNGestureHandlerDetector": "RNGestureHandlerDetector", + "RNGestureHandlerButtonWrapper": "RNGestureHandlerButtonWrapper" } } }, diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h index d9f3374c48..5621db20e7 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h @@ -15,12 +15,14 @@ #include #include +#include #include namespace facebook::react { using RNGestureHandlerButtonComponentDescriptor = ConcreteComponentDescriptor; + using RNGestureHandlerRootViewComponentDescriptor = ConcreteComponentDescriptor; diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperComponentDescriptor.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperComponentDescriptor.h new file mode 100644 index 0000000000..c3323ae823 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperComponentDescriptor.h @@ -0,0 +1,32 @@ + +/** + * This code was generated by + * [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be + * lost once the code is regenerated. + * + * @generated by codegen project: GenerateComponentDescriptorH.js + */ + +#pragma once + +#include + +#include "RNGestureHandlerButtonWrapperShadowNode.h" + +namespace facebook::react { + +class RNGestureHandlerButtonWrapperComponentDescriptor final + : public ConcreteComponentDescriptor< + RNGestureHandlerButtonWrapperShadowNode> { + using ConcreteComponentDescriptor::ConcreteComponentDescriptor; + void adopt(ShadowNode &shadowNode) const override { + react_native_assert( + dynamic_cast(&shadowNode)); + + ConcreteComponentDescriptor::adopt(shadowNode); + } +}; + +} // namespace facebook::react diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp new file mode 100644 index 0000000000..e8a45f5caf --- /dev/null +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp @@ -0,0 +1,59 @@ + +/** + * This code was generated by + * [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be + * lost once the code is regenerated. + * + * @generated by codegen project: GenerateShadowNodeCpp.js + */ + +#include "RNGestureHandlerButtonWrapperShadowNode.h" + +namespace facebook::react { + +extern const char RNGestureHandlerButtonWrapperComponentName[] = + "RNGestureHandlerButtonWrapper"; + +void RNGestureHandlerButtonWrapperShadowNode::initialize() { + // Disable forcing view flattening + ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView); + + // When the button wrapper is cloned and has a child node, the child node + // should be cloned as well to ensure it is mutable. + const auto &children = getChildren(); + if (!children.empty()) { + react_native_assert( + children.size() == 1 && + "RNGestureHandlerButtonWrapper received more than one child"); + + const auto clonedChild = children[0]->clone({}); + replaceChild(*children[0], clonedChild); + } +} + +void RNGestureHandlerButtonWrapperShadowNode::layout( + LayoutContext layoutContext) { + YogaLayoutableShadowNode::layout(layoutContext); + // TODO: consider allowing more than one child and doing bounding box + react_native_assert(getChildren().size() == 1); + + auto child = std::static_pointer_cast( + getChildren()[0]); + + child->ensureUnsealed(); + auto mutableChild = std::const_pointer_cast(child); + + // TODO: figure out the correct way to setup metrics between button wrapper + // and the child + auto metrics = child->getLayoutMetrics(); + metrics.frame = child->getLayoutMetrics().frame; + setLayoutMetrics(metrics); + + auto childmetrics = child->getLayoutMetrics(); + childmetrics.frame.origin = Point{}; + mutableChild->setLayoutMetrics(childmetrics); +} + +} // namespace facebook::react diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h new file mode 100644 index 0000000000..ef0a0ca2ff --- /dev/null +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h @@ -0,0 +1,57 @@ + +/** + * This code was generated by + * [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be + * lost once the code is regenerated. + * + * @generated by codegen project: GenerateShadowNodeH.js + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "RNGestureHandlerButtonWrapperState.h" + +namespace facebook::react { + +JSI_EXPORT extern const char RNGestureHandlerButtonWrapperComponentName[]; + +/* + * `ShadowNode` for component. + */ +class RNGestureHandlerButtonWrapperShadowNode final + : public ConcreteViewShadowNode< + RNGestureHandlerButtonWrapperComponentName, + RNGestureHandlerButtonWrapperProps, + RNGestureHandlerButtonWrapperEventEmitter, + RNGestureHandlerButtonWrapperState> { + public: + RNGestureHandlerButtonWrapperShadowNode( + const ShadowNodeFragment &fragment, + const ShadowNodeFamily::Shared &family, + ShadowNodeTraits traits) + : ConcreteViewShadowNode(fragment, family, traits) { + initialize(); + } + + RNGestureHandlerButtonWrapperShadowNode( + const ShadowNode &sourceShadowNode, + const ShadowNodeFragment &fragment) + : ConcreteViewShadowNode(sourceShadowNode, fragment) { + initialize(); + } + + void layout(LayoutContext layoutContext) override; + + private: + void initialize(); +}; + +} // namespace facebook::react diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperState.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperState.h new file mode 100644 index 0000000000..7d9fa242e0 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperState.h @@ -0,0 +1,32 @@ +/** + * This code was generated by + * [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be + * lost once the code is regenerated. + * + * @generated by codegen project: GenerateStateH.js + */ +#pragma once + +#ifdef ANDROID +#include +#endif + +namespace facebook::react { + +class RNGestureHandlerButtonWrapperState { + public: + RNGestureHandlerButtonWrapperState() = default; + +#ifdef ANDROID + RNGestureHandlerButtonWrapperState( + RNGestureHandlerButtonWrapperState const &previousState, + folly::dynamic data){}; + folly::dynamic getDynamic() const { + return {}; + }; +#endif +}; + +} // namespace facebook::react diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonWrapperNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonWrapperNativeComponent.ts new file mode 100644 index 0000000000..d84e610aaa --- /dev/null +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonWrapperNativeComponent.ts @@ -0,0 +1,11 @@ +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +import type { ViewProps } from 'react-native'; + +interface NativeProps extends ViewProps {} + +export default codegenNativeComponent( + 'RNGestureHandlerButtonWrapper', + { + interfaceOnly: true, + } +); From 3f6f084b5d914e0ce453d469d4f709b87f937d05 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 24 Jul 2025 08:37:40 +0200 Subject: [PATCH 02/12] Implement iOS view --- .../apple/RNGestureHandlerButtonWrapper.h | 19 ++++++++ .../apple/RNGestureHandlerButtonWrapper.mm | 48 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.h create mode 100644 packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.mm diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.h new file mode 100644 index 0000000000..c94ecf946e --- /dev/null +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.h @@ -0,0 +1,19 @@ +#if !TARGET_OS_OSX +#import +#else +#import +#endif + +#import + +#import + +using namespace facebook::react; + +NS_ASSUME_NONNULL_BEGIN + +@interface RNGestureHandlerButtonWrapper : RCTViewComponentView + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.mm new file mode 100644 index 0000000000..308b69ac76 --- /dev/null +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.mm @@ -0,0 +1,48 @@ +#import "RNGestureHandlerButtonWrapper.h" +#import "RNGestureHandlerButtonWrapperComponentDescriptor.h" +#import "RNGestureHandlerModule.h" + +#import +#import + +#import +#import +#import + +#include + +@interface RNGestureHandlerButtonWrapper () +@end + +@implementation RNGestureHandlerButtonWrapper + +#if TARGET_OS_OSX ++ (BOOL)shouldBeRecycled +{ + return NO; +} +#endif + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + static const auto defaultProps = std::make_shared(); + _props = defaultProps; + } + + return self; +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +@end + +Class RNGestureHandlerButtonWrapperCls(void) +{ + return RNGestureHandlerButtonWrapper.class; +} From c1f19d8db8f14b32d8c39cd5927a16799d40df90 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 24 Jul 2025 09:01:13 +0200 Subject: [PATCH 03/12] Implement Android view --- .../gesturehandler/RNGestureHandlerPackage.kt | 5 +++ .../RNGestureHandlerButtonWrapperView.kt | 6 ++++ ...NGestureHandlerButtonWrapperViewManager.kt | 33 +++++++++++++++++++ .../react-native.config.js | 5 ++- 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperView.kt create mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperViewManager.kt diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt index 95f071671f..d5c7001e20 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt @@ -11,6 +11,7 @@ import com.facebook.react.module.model.ReactModuleInfo import com.facebook.react.module.model.ReactModuleInfoProvider import com.facebook.react.uimanager.ViewManager import com.swmansion.gesturehandler.react.RNGestureHandlerButtonViewManager +import com.swmansion.gesturehandler.react.RNGestureHandlerButtonWrapperViewManager import com.swmansion.gesturehandler.react.RNGestureHandlerDetectorViewManager import com.swmansion.gesturehandler.react.RNGestureHandlerModule import com.swmansion.gesturehandler.react.RNGestureHandlerRootViewManager @@ -34,6 +35,9 @@ class RNGestureHandlerPackage : RNGestureHandlerDetectorViewManager.REACT_CLASS to ModuleSpec.viewManagerSpec { RNGestureHandlerDetectorViewManager() }, + RNGestureHandlerButtonWrapperViewManager.REACT_CLASS to ModuleSpec.viewManagerSpec { + RNGestureHandlerButtonWrapperViewManager() + }, ) } @@ -41,6 +45,7 @@ class RNGestureHandlerPackage : RNGestureHandlerRootViewManager(), RNGestureHandlerButtonViewManager(), RNGestureHandlerDetectorViewManager(), + RNGestureHandlerButtonWrapperViewManager(), ) override fun getViewManagerNames(reactContext: ReactApplicationContext) = viewManagers.keys.toList() diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperView.kt new file mode 100644 index 0000000000..f6b899968d --- /dev/null +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperView.kt @@ -0,0 +1,6 @@ +package com.swmansion.gesturehandler.react + +import android.content.Context +import com.facebook.react.views.view.ReactViewGroup + +class RNGestureHandlerButtonWrapperView(context: Context) : ReactViewGroup(context) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperViewManager.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperViewManager.kt new file mode 100644 index 0000000000..617d7b0877 --- /dev/null +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperViewManager.kt @@ -0,0 +1,33 @@ +package com.swmansion.gesturehandler.react + +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate +import com.facebook.react.viewmanagers.RNGestureHandlerButtonWrapperManagerDelegate +import com.facebook.react.viewmanagers.RNGestureHandlerButtonWrapperManagerInterface + +@ReactModule(name = RNGestureHandlerButtonWrapperViewManager.REACT_CLASS) +class RNGestureHandlerButtonWrapperViewManager : + ViewGroupManager(), + RNGestureHandlerButtonWrapperManagerInterface { + private val mDelegate: ViewManagerDelegate + + init { + mDelegate = + RNGestureHandlerButtonWrapperManagerDelegate< + RNGestureHandlerButtonWrapperView, + RNGestureHandlerButtonWrapperViewManager, + >(this) + } + + override fun getDelegate(): ViewManagerDelegate = mDelegate + + override fun getName() = REACT_CLASS + + override fun createViewInstance(reactContext: ThemedReactContext) = RNGestureHandlerButtonWrapperView(reactContext) + + companion object { + const val REACT_CLASS = "RNGestureHandlerButtonWrapper" + } +} diff --git a/packages/react-native-gesture-handler/react-native.config.js b/packages/react-native-gesture-handler/react-native.config.js index 4ec41154cd..46e3de4682 100644 --- a/packages/react-native-gesture-handler/react-native.config.js +++ b/packages/react-native-gesture-handler/react-native.config.js @@ -2,7 +2,10 @@ module.exports = { dependency: { platforms: { android: { - componentDescriptors: ['RNGestureHandlerDetectorComponentDescriptor'], + componentDescriptors: [ + 'RNGestureHandlerDetectorComponentDescriptor', + 'RNGestureHandlerButtonWrapperComponentDescriptor', + ], cmakeListsPath: './CMakeLists.txt', }, }, From 04ef62a9b6168229235c602bdac307b34f6d1fb8 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 24 Jul 2025 12:27:35 +0200 Subject: [PATCH 04/12] What are you? A button sandwitch. --- ...NGestureHandlerButtonWrapperShadowNode.cpp | 60 +++++++++++++++---- .../RNGestureHandlerButtonWrapperShadowNode.h | 2 + .../src/components/GestureHandlerButton.tsx | 29 ++++++++- 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp index e8a45f5caf..7382a63237 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp @@ -9,6 +9,8 @@ * @generated by codegen project: GenerateShadowNodeCpp.js */ +#include + #include "RNGestureHandlerButtonWrapperShadowNode.h" namespace facebook::react { @@ -22,38 +24,70 @@ void RNGestureHandlerButtonWrapperShadowNode::initialize() { // When the button wrapper is cloned and has a child node, the child node // should be cloned as well to ensure it is mutable. + if (!getChildren().empty()) { + prepareChildren(); + } +} + +void RNGestureHandlerButtonWrapperShadowNode::prepareChildren() { const auto &children = getChildren(); - if (!children.empty()) { - react_native_assert( - children.size() == 1 && - "RNGestureHandlerButtonWrapper received more than one child"); + react_native_assert( + children.size() == 1 && + "RNGestureHandlerButtonWrapper received more than one child"); - const auto clonedChild = children[0]->clone({}); - replaceChild(*children[0], clonedChild); - } + const auto directChild = children[0]; + react_native_assert( + directChild->getChildren().size() == 1 && + "RNGestureHandlerButtonWrapper received more than one grandchild"); + + const auto clonedChild = directChild->clone({}); + + const auto childWithProtectedAccess = + std::static_pointer_cast( + clonedChild); + childWithProtectedAccess->traits_.unset(ShadowNodeTraits::ForceFlattenView); + + replaceChild(*directChild, clonedChild); + + const auto grandChild = clonedChild->getChildren()[0]; + const auto clonedGrandChild = grandChild->clone({}); + clonedChild->replaceChild(*grandChild, clonedGrandChild); +} + +void RNGestureHandlerButtonWrapperShadowNode::appendChild( + const ShadowNode::Shared &child) { + YogaLayoutableShadowNode::appendChild(child); + prepareChildren(); } void RNGestureHandlerButtonWrapperShadowNode::layout( LayoutContext layoutContext) { YogaLayoutableShadowNode::layout(layoutContext); - // TODO: consider allowing more than one child and doing bounding box react_native_assert(getChildren().size() == 1); + react_native_assert(getChildren()[0]->getChildren().size() == 1); auto child = std::static_pointer_cast( getChildren()[0]); + auto grandChild = std::static_pointer_cast( + child->getChildren()[0]); child->ensureUnsealed(); + grandChild->ensureUnsealed(); + auto mutableChild = std::const_pointer_cast(child); + auto mutableGrandChild = + std::const_pointer_cast(grandChild); // TODO: figure out the correct way to setup metrics between button wrapper // and the child - auto metrics = child->getLayoutMetrics(); - metrics.frame = child->getLayoutMetrics().frame; + auto metrics = grandChild->getLayoutMetrics(); setLayoutMetrics(metrics); - auto childmetrics = child->getLayoutMetrics(); - childmetrics.frame.origin = Point{}; - mutableChild->setLayoutMetrics(childmetrics); + auto metricsNoOrigin = grandChild->getLayoutMetrics(); + metricsNoOrigin.frame.origin = Point{}; + + mutableChild->setLayoutMetrics(metricsNoOrigin); + mutableGrandChild->setLayoutMetrics(metricsNoOrigin); } } // namespace facebook::react diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h index ef0a0ca2ff..bc83e7a2a3 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h @@ -49,9 +49,11 @@ class RNGestureHandlerButtonWrapperShadowNode final } void layout(LayoutContext layoutContext) override; + void appendChild(const ShadowNode::Shared &child) override; private: void initialize(); + void prepareChildren(); }; } // namespace facebook::react diff --git a/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx b/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx index b6f0c391c0..40b2f7ea58 100644 --- a/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx +++ b/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx @@ -1,5 +1,30 @@ -import { HostComponent } from 'react-native'; +import { HostComponent, StyleSheet, View } from 'react-native'; import type { RawButtonProps } from './GestureButtonsProps'; import RNGestureHandlerButtonNativeComponent from '../specs/RNGestureHandlerButtonNativeComponent'; +import RNGestureHandlerButtonWrapperNativeComponent from '../specs/RNGestureHandlerButtonWrapperNativeComponent'; -export default RNGestureHandlerButtonNativeComponent as HostComponent; +const ButtonComponent = + RNGestureHandlerButtonNativeComponent as HostComponent; + +export default function GestureHandlerButton({ + style, + ...rest +}: RawButtonProps) { + const flattenedStyle = StyleSheet.flatten(style); + + const { width, height, ...restStyle } = flattenedStyle; + + return ( + + + + + + ); +} + +const styles = StyleSheet.create({ + contents: { + display: 'contents', + }, +}); From 6e41f43ac982a86f484d0c6896894b1849fb2a75 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 24 Jul 2025 15:43:04 +0200 Subject: [PATCH 05/12] Style splitting --- apps/basic-example/src/RuntimeDecoration.tsx | 580 +++++++++++++++--- .../src/components/GestureHandlerButton.tsx | 129 +++- 2 files changed, 602 insertions(+), 107 deletions(-) diff --git a/apps/basic-example/src/RuntimeDecoration.tsx b/apps/basic-example/src/RuntimeDecoration.tsx index d3baa9eb3c..540cf1ef1c 100644 --- a/apps/basic-example/src/RuntimeDecoration.tsx +++ b/apps/basic-example/src/RuntimeDecoration.tsx @@ -1,105 +1,190 @@ -import * as React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; -import { Gesture, GestureDetector } from 'react-native-gesture-handler'; - -import { COLORS } from './colors'; -import Animated, { - runOnJS, - useAnimatedStyle, - useSharedValue, -} from 'react-native-reanimated'; -import { useState } from 'react'; - -export function RuntimeChecker({ - runGestureOnJS, -}: { - runGestureOnJS: boolean; -}) { - const pressed = useSharedValue(false); - const active = useSharedValue(false); - const posX = useSharedValue(0); - const posY = useSharedValue(0); - - const start = useSharedValue({ x: 0, y: 0 }); - const [isUIRuntime, setIsUIRuntime] = useState(null); - - const style = useAnimatedStyle(() => { - return { - transform: [ - { translateX: posX.value }, - { translateY: posY.value }, - { scale: pressed.value ? 1.2 : 1 }, - ], - backgroundColor: active.value ? COLORS.KINDA_GREEN : COLORS.KINDA_BLUE, - }; - }); - - const gesture = Gesture.Manual() - .runOnJS(runGestureOnJS) - .onTouchesDown((e) => { - if (!pressed.value) { - pressed.value = true; - start.value = { - x: e.allTouches[0].absoluteX, - y: e.allTouches[0].absoluteY, - }; - } - runOnJS(setIsUIRuntime)(_WORKLET ?? false); - }) - .onTouchesMove((e, state) => { - const dist = Math.sqrt( - Math.pow(e.allTouches[0].absoluteX - start.value.x, 2) + - Math.pow(e.allTouches[0].absoluteY - start.value.y, 2) - ); - - if (active.value) { - posX.value = e.allTouches[0].absoluteX - start.value.x; - posY.value = e.allTouches[0].absoluteY - start.value.y; - } else { - if (dist > 10) { - state.activate(); - start.value = { - x: e.allTouches[0].absoluteX, - y: e.allTouches[0].absoluteY, - }; - } - } - }) - .onTouchesUp((e, state) => { - if (e.allTouches.length === e.changedTouches.length) { - state.end(); - } - }) - .onStart(() => { - active.value = true; - }) - .onFinalize(() => { - pressed.value = false; - active.value = false; - }); +import React from 'react'; +import { View, StyleSheet, Text, SafeAreaView } from 'react-native'; +import { + GestureHandlerRootView, + ScrollView, + RectButton, +} from 'react-native-gesture-handler'; +const MyButton = RectButton; + +export default function ComplexUI() { + return ( + + + + + + + + + + + + + + + + + ); +} + +const colors = ['#782AEB', '#38ACDD', '#57B495', '#FF6259', '#FFD61E']; + +function Avatars() { + return ( + + {colors.map((color) => ( + + {color.slice(1, 3)} + + ))} + + ); +} + +function Gallery() { + return ( + + Basic Gallery + + + + + + + + + + ); +} + +function SizeConstraints() { return ( - - - {isUIRuntime === null - ? 'Start the gesture to check the runtime' - : `Running on the ${isUIRuntime ? 'UI' : 'JS'} runtime`} - - - - + + Size Constraints + + + Min/Max + + + 1:1 + + + Flex + + ); } -export default function RuntimeDecorationExample() { +function FlexboxTests() { return ( - - - - + + Flexbox Layouts + + + Start + + + Center + + + End + + + + + Wrap 1 + + + Wrap 2 + + + Wrap 3 + + + Wrap 4 + + + + ); +} + +function PositioningTests() { + return ( + + Positioning + + + Z-Index + + + Absolute + + + Relative + + + + ); +} + +function SpacingTests() { + return ( + + Spacing & Overflow + + + Padding + + + Margin + + + Overflow Hidden Test + + + + ); +} + +function VisualEffects() { + return ( + + Visual Effects + + + Shadow + + + Opacity + + + + ); +} + +function ComplexCombinations() { + return ( + + Complex Combinations + + + Complex 1 + + + Complex 2 + + + Complex 3 + + + Complex 4 + + ); } @@ -107,17 +192,308 @@ export default function RuntimeDecorationExample() { const styles = StyleSheet.create({ container: { flex: 1, + backgroundColor: '#f5f5f5', + }, + scrollContent: { + paddingBottom: 50, + }, + paddedContainer: { + padding: 16, + }, + section: { + marginBottom: 30, + }, + sectionTitle: { + fontSize: 18, + fontWeight: 'bold', + marginBottom: 12, + color: '#333', + }, + gap: { + gap: 10, + }, + row: { + flexDirection: 'row', + }, + + // Avatar styles + avatars: { + width: 90, + height: 90, + borderWidth: 2, + borderColor: '#001A72', + borderTopLeftRadius: 30, + borderTopRightRadius: 5, + borderBottomLeftRadius: 5, + borderBottomRightRadius: 30, + marginHorizontal: 4, alignItems: 'center', justifyContent: 'center', - gap: 8, }, - box: { - width: 50, - height: 50, + avatarLabel: { + color: '#F8F9FF', + fontSize: 24, + fontWeight: 'bold', }, - separator: { + + // Gallery styles + fullWidthButton: { width: '100%', - height: 1, - backgroundColor: 'black', + height: 160, + backgroundColor: '#FF6259', + borderTopRightRadius: 30, + borderTopLeftRadius: 30, + borderWidth: 1, + borderColor: '#000', + }, + leftButton: { + flex: 1, + height: 160, + backgroundColor: '#FFD61E', + borderBottomLeftRadius: 30, + borderWidth: 5, + borderColor: '#000', + }, + rightButton: { + flex: 1, + backgroundColor: '#782AEB', + height: 160, + borderBottomRightRadius: 30, + borderWidth: 8, + borderColor: '#000', + }, + + // Size constraint styles + minMaxButton: { + minWidth: 80, + maxWidth: 120, + minHeight: 40, + maxHeight: 80, + backgroundColor: '#38ACDD', + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + }, + aspectRatioButton: { + width: 80, + aspectRatio: 1, + backgroundColor: '#57B495', + borderRadius: 40, + justifyContent: 'center', + alignItems: 'center', + }, + flexGrowButton: { + flexGrow: 1, + height: 60, + backgroundColor: '#FF6259', + borderRadius: 15, + justifyContent: 'center', + alignItems: 'center', + }, + + // Flexbox styles + flexContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + height: 80, + backgroundColor: '#e0e0e0', + borderRadius: 10, + padding: 10, + marginBottom: 10, + }, + flexStart: { + alignSelf: 'flex-start', + backgroundColor: '#782AEB', + padding: 10, + borderRadius: 5, + }, + flexCenter: { + alignSelf: 'center', + backgroundColor: '#38ACDD', + padding: 10, + borderRadius: 5, + }, + flexEnd: { + alignSelf: 'flex-end', + backgroundColor: '#57B495', + padding: 10, + borderRadius: 5, + }, + flexWrapContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 5, + }, + wrapItem: { + width: '48%', + height: 50, + backgroundColor: '#FFD61E', + borderRadius: 8, + justifyContent: 'center', + alignItems: 'center', + }, + + // Positioning styles + positionContainer: { + height: 80, + backgroundColor: '#e0e0e0', + borderRadius: 10, + position: 'relative', + }, + absoluteButton: { + position: 'absolute', + top: 10, + right: 10, + backgroundColor: '#FF6259', + padding: 8, + borderRadius: 5, + }, + relativeButton: { + position: 'relative', + top: 20, + left: 20, + backgroundColor: '#782AEB', + padding: 8, + borderRadius: 5, + }, + zIndexButton: { + position: 'absolute', + top: 10, + left: 10, + zIndex: 10, + backgroundColor: '#57B495', + padding: 8, + borderRadius: 5, + }, + + // Spacing styles + paddingButton: { + flex: 1, + backgroundColor: '#38ACDD', + paddingVertical: 20, + paddingHorizontal: 15, + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + }, + marginButton: { + flex: 1, + backgroundColor: '#FFD61E', + margin: 10, + padding: 10, + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + }, + overflowButton: { + flex: 1, + height: 60, + backgroundColor: '#782AEB', + borderRadius: 10, + overflow: 'hidden', + justifyContent: 'center', + alignItems: 'center', + }, + + // Visual effect styles + shadowButton: { + flex: 1, + height: 60, + backgroundColor: '#FF6259', + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 8, + elevation: 8, + }, + opacityButton: { + flex: 1, + height: 60, + backgroundColor: '#782AEB', + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + opacity: 0.7, + }, + + // Complex combination styles + complexGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 10, + }, + complexButton1: { + width: '48%', + height: 100, + backgroundColor: '#FF6259', + borderRadius: 20, + borderWidth: 2, + borderColor: '#782AEB', + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 2, height: 2 }, + shadowOpacity: 0.2, + shadowRadius: 4, + elevation: 4, + marginBottom: 5, + }, + complexButton2: { + width: '48%', + minHeight: 80, + maxHeight: 120, + backgroundColor: '#38ACDD', + borderTopLeftRadius: 30, + borderBottomRightRadius: 30, + paddingVertical: 15, + paddingHorizontal: 10, + justifyContent: 'center', + alignItems: 'center', + overflow: 'hidden', + }, + complexButton3: { + width: '48%', + aspectRatio: 1.5, + backgroundColor: '#57B495', + borderRadius: 15, + borderWidth: 4, + borderColor: '#FFD61E', + justifyContent: 'center', + alignItems: 'center', + opacity: 0.9, + marginTop: 10, + }, + complexButton4: { + width: '48%', + height: 80, + backgroundColor: '#FFD61E', + borderRadius: 10, + borderWidth: 1, + borderColor: '#FF6259', + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#782AEB', + shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.4, + shadowRadius: 10, + elevation: 10, + marginTop: 10, + }, + + // Text styles + buttonText: { + color: 'white', + fontWeight: 'bold', + textAlign: 'center', + }, + longText: { + color: 'white', + fontWeight: 'bold', + textAlign: 'center', + fontSize: 16, }, }); diff --git a/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx b/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx index 40b2f7ea58..51fc9bdf4c 100644 --- a/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx +++ b/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx @@ -2,6 +2,7 @@ import { HostComponent, StyleSheet, View } from 'react-native'; import type { RawButtonProps } from './GestureButtonsProps'; import RNGestureHandlerButtonNativeComponent from '../specs/RNGestureHandlerButtonNativeComponent'; import RNGestureHandlerButtonWrapperNativeComponent from '../specs/RNGestureHandlerButtonWrapperNativeComponent'; +import { useMemo } from 'react'; const ButtonComponent = RNGestureHandlerButtonNativeComponent as HostComponent; @@ -10,14 +11,129 @@ export default function GestureHandlerButton({ style, ...rest }: RawButtonProps) { - const flattenedStyle = StyleSheet.flatten(style); + const flattenedStyle = useMemo(() => StyleSheet.flatten(style), [style]); - const { width, height, ...restStyle } = flattenedStyle; + const { + // Layout properties + display, + width, + height, + minWidth, + maxWidth, + minHeight, + maxHeight, + flex, + flexGrow, + flexShrink, + flexBasis, + flexDirection, + flexWrap, + justifyContent, + alignItems, + alignContent, + alignSelf, + aspectRatio, + gap, + rowGap, + columnGap, + margin, + marginTop, + marginBottom, + marginLeft, + marginRight, + marginVertical, + marginHorizontal, + marginStart, + marginEnd, + padding, + paddingTop, + paddingBottom, + paddingLeft, + paddingRight, + paddingVertical, + paddingHorizontal, + paddingStart, + paddingEnd, + position, + top, + right, + bottom, + left, + start, + end, + overflow, + zIndex, + + // Visual properties + ...restStyle + } = flattenedStyle; + + // Layout styles for ButtonComponent + const layoutStyle = useMemo( + () => ({ + display, + width, + height, + minWidth, + maxWidth, + minHeight, + maxHeight, + flex, + flexGrow, + flexShrink, + flexBasis, + flexDirection, + flexWrap, + justifyContent, + alignItems, + alignContent, + alignSelf, + aspectRatio, + gap, + rowGap, + columnGap, + margin, + marginTop, + marginBottom, + marginLeft, + marginRight, + marginVertical, + marginHorizontal, + marginStart, + marginEnd, + padding, + paddingTop, + paddingBottom, + paddingLeft, + paddingRight, + paddingVertical, + paddingHorizontal, + paddingStart, + paddingEnd, + position, + top, + right, + bottom, + left, + start, + end, + overflow, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [flattenedStyle] + ); return ( - - - + + + ); @@ -27,4 +143,7 @@ const styles = StyleSheet.create({ contents: { display: 'contents', }, + overflowHidden: { + overflow: 'hidden', + }, }); From d0dad08374eeb195b15350804829368f48920bd6 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 12 Sep 2025 10:16:16 +0200 Subject: [PATCH 06/12] Adapt to 81 --- apps/basic-example/ios/Podfile.lock | 2 +- .../RNGestureHandlerButtonWrapperShadowNode.cpp | 2 +- .../RNGestureHandlerButtonWrapperShadowNode.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/basic-example/ios/Podfile.lock b/apps/basic-example/ios/Podfile.lock index 437a73f3f0..bef9129b57 100644 --- a/apps/basic-example/ios/Podfile.lock +++ b/apps/basic-example/ios/Podfile.lock @@ -2743,7 +2743,7 @@ SPEC CHECKSUMS: RNReanimated: 3dd9b5776c0e9741fbbf1a4a54dd4328924b4a1e RNWorklets: 09a47bd92d3da6e00420a060fcc86e23b745e980 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 00013dd9cde63a2d98e8002fcc4f5ddb66c10782 + Yoga: b01392348aeea02064c21a2762a42893d82b60a7 PODFILE CHECKSUM: d05778d3a61b8d49242579ea0aa864580fbb1f64 diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp index 7382a63237..3fa4911dae 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp @@ -55,7 +55,7 @@ void RNGestureHandlerButtonWrapperShadowNode::prepareChildren() { } void RNGestureHandlerButtonWrapperShadowNode::appendChild( - const ShadowNode::Shared &child) { + const std::shared_ptr &child) { YogaLayoutableShadowNode::appendChild(child); prepareChildren(); } diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h index bc83e7a2a3..1c53c1491b 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h @@ -49,7 +49,7 @@ class RNGestureHandlerButtonWrapperShadowNode final } void layout(LayoutContext layoutContext) override; - void appendChild(const ShadowNode::Shared &child) override; + void appendChild(const std::shared_ptr &child) override; private: void initialize(); From 921787798d6d498a6a16f7535f66d514b60339b1 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 12 Sep 2025 10:25:03 +0200 Subject: [PATCH 07/12] Move example to a separate screen --- apps/basic-example/src/App.tsx | 5 + apps/basic-example/src/ContentsButton.tsx | 499 ++++++++++++++++ apps/basic-example/src/RuntimeDecoration.tsx | 580 ++++--------------- 3 files changed, 606 insertions(+), 478 deletions(-) create mode 100644 apps/basic-example/src/ContentsButton.tsx diff --git a/apps/basic-example/src/App.tsx b/apps/basic-example/src/App.tsx index fceb9a2ba2..c2c3714337 100644 --- a/apps/basic-example/src/App.tsx +++ b/apps/basic-example/src/App.tsx @@ -6,6 +6,7 @@ import Navigator from './Navigator'; import NativeDetector from './NativeDetector'; import RuntimeDecoration from './RuntimeDecoration'; +import ContentsButton from './ContentsButton'; const EXAMPLES = [ { @@ -16,6 +17,10 @@ const EXAMPLES = [ name: 'Native Detector', component: NativeDetector, }, + { + name: 'Contents Button', + component: ContentsButton, + }, ]; const Stack = Navigator.create(); diff --git a/apps/basic-example/src/ContentsButton.tsx b/apps/basic-example/src/ContentsButton.tsx new file mode 100644 index 0000000000..540cf1ef1c --- /dev/null +++ b/apps/basic-example/src/ContentsButton.tsx @@ -0,0 +1,499 @@ +import React from 'react'; +import { View, StyleSheet, Text, SafeAreaView } from 'react-native'; +import { + GestureHandlerRootView, + ScrollView, + RectButton, +} from 'react-native-gesture-handler'; + +const MyButton = RectButton; + +export default function ComplexUI() { + return ( + + + + + + + + + + + + + + + + + ); +} + +const colors = ['#782AEB', '#38ACDD', '#57B495', '#FF6259', '#FFD61E']; + +function Avatars() { + return ( + + {colors.map((color) => ( + + {color.slice(1, 3)} + + ))} + + ); +} + +function Gallery() { + return ( + + Basic Gallery + + + + + + + + + + ); +} + +function SizeConstraints() { + return ( + + Size Constraints + + + Min/Max + + + 1:1 + + + Flex + + + + ); +} + +function FlexboxTests() { + return ( + + Flexbox Layouts + + + Start + + + Center + + + End + + + + + Wrap 1 + + + Wrap 2 + + + Wrap 3 + + + Wrap 4 + + + + ); +} + +function PositioningTests() { + return ( + + Positioning + + + Z-Index + + + Absolute + + + Relative + + + + ); +} + +function SpacingTests() { + return ( + + Spacing & Overflow + + + Padding + + + Margin + + + Overflow Hidden Test + + + + ); +} + +function VisualEffects() { + return ( + + Visual Effects + + + Shadow + + + Opacity + + + + ); +} + +function ComplexCombinations() { + return ( + + Complex Combinations + + + Complex 1 + + + Complex 2 + + + Complex 3 + + + Complex 4 + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#f5f5f5', + }, + scrollContent: { + paddingBottom: 50, + }, + paddedContainer: { + padding: 16, + }, + section: { + marginBottom: 30, + }, + sectionTitle: { + fontSize: 18, + fontWeight: 'bold', + marginBottom: 12, + color: '#333', + }, + gap: { + gap: 10, + }, + row: { + flexDirection: 'row', + }, + + // Avatar styles + avatars: { + width: 90, + height: 90, + borderWidth: 2, + borderColor: '#001A72', + borderTopLeftRadius: 30, + borderTopRightRadius: 5, + borderBottomLeftRadius: 5, + borderBottomRightRadius: 30, + marginHorizontal: 4, + alignItems: 'center', + justifyContent: 'center', + }, + avatarLabel: { + color: '#F8F9FF', + fontSize: 24, + fontWeight: 'bold', + }, + + // Gallery styles + fullWidthButton: { + width: '100%', + height: 160, + backgroundColor: '#FF6259', + borderTopRightRadius: 30, + borderTopLeftRadius: 30, + borderWidth: 1, + borderColor: '#000', + }, + leftButton: { + flex: 1, + height: 160, + backgroundColor: '#FFD61E', + borderBottomLeftRadius: 30, + borderWidth: 5, + borderColor: '#000', + }, + rightButton: { + flex: 1, + backgroundColor: '#782AEB', + height: 160, + borderBottomRightRadius: 30, + borderWidth: 8, + borderColor: '#000', + }, + + // Size constraint styles + minMaxButton: { + minWidth: 80, + maxWidth: 120, + minHeight: 40, + maxHeight: 80, + backgroundColor: '#38ACDD', + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + }, + aspectRatioButton: { + width: 80, + aspectRatio: 1, + backgroundColor: '#57B495', + borderRadius: 40, + justifyContent: 'center', + alignItems: 'center', + }, + flexGrowButton: { + flexGrow: 1, + height: 60, + backgroundColor: '#FF6259', + borderRadius: 15, + justifyContent: 'center', + alignItems: 'center', + }, + + // Flexbox styles + flexContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + height: 80, + backgroundColor: '#e0e0e0', + borderRadius: 10, + padding: 10, + marginBottom: 10, + }, + flexStart: { + alignSelf: 'flex-start', + backgroundColor: '#782AEB', + padding: 10, + borderRadius: 5, + }, + flexCenter: { + alignSelf: 'center', + backgroundColor: '#38ACDD', + padding: 10, + borderRadius: 5, + }, + flexEnd: { + alignSelf: 'flex-end', + backgroundColor: '#57B495', + padding: 10, + borderRadius: 5, + }, + flexWrapContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 5, + }, + wrapItem: { + width: '48%', + height: 50, + backgroundColor: '#FFD61E', + borderRadius: 8, + justifyContent: 'center', + alignItems: 'center', + }, + + // Positioning styles + positionContainer: { + height: 80, + backgroundColor: '#e0e0e0', + borderRadius: 10, + position: 'relative', + }, + absoluteButton: { + position: 'absolute', + top: 10, + right: 10, + backgroundColor: '#FF6259', + padding: 8, + borderRadius: 5, + }, + relativeButton: { + position: 'relative', + top: 20, + left: 20, + backgroundColor: '#782AEB', + padding: 8, + borderRadius: 5, + }, + zIndexButton: { + position: 'absolute', + top: 10, + left: 10, + zIndex: 10, + backgroundColor: '#57B495', + padding: 8, + borderRadius: 5, + }, + + // Spacing styles + paddingButton: { + flex: 1, + backgroundColor: '#38ACDD', + paddingVertical: 20, + paddingHorizontal: 15, + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + }, + marginButton: { + flex: 1, + backgroundColor: '#FFD61E', + margin: 10, + padding: 10, + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + }, + overflowButton: { + flex: 1, + height: 60, + backgroundColor: '#782AEB', + borderRadius: 10, + overflow: 'hidden', + justifyContent: 'center', + alignItems: 'center', + }, + + // Visual effect styles + shadowButton: { + flex: 1, + height: 60, + backgroundColor: '#FF6259', + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 8, + elevation: 8, + }, + opacityButton: { + flex: 1, + height: 60, + backgroundColor: '#782AEB', + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + opacity: 0.7, + }, + + // Complex combination styles + complexGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 10, + }, + complexButton1: { + width: '48%', + height: 100, + backgroundColor: '#FF6259', + borderRadius: 20, + borderWidth: 2, + borderColor: '#782AEB', + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 2, height: 2 }, + shadowOpacity: 0.2, + shadowRadius: 4, + elevation: 4, + marginBottom: 5, + }, + complexButton2: { + width: '48%', + minHeight: 80, + maxHeight: 120, + backgroundColor: '#38ACDD', + borderTopLeftRadius: 30, + borderBottomRightRadius: 30, + paddingVertical: 15, + paddingHorizontal: 10, + justifyContent: 'center', + alignItems: 'center', + overflow: 'hidden', + }, + complexButton3: { + width: '48%', + aspectRatio: 1.5, + backgroundColor: '#57B495', + borderRadius: 15, + borderWidth: 4, + borderColor: '#FFD61E', + justifyContent: 'center', + alignItems: 'center', + opacity: 0.9, + marginTop: 10, + }, + complexButton4: { + width: '48%', + height: 80, + backgroundColor: '#FFD61E', + borderRadius: 10, + borderWidth: 1, + borderColor: '#FF6259', + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#782AEB', + shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.4, + shadowRadius: 10, + elevation: 10, + marginTop: 10, + }, + + // Text styles + buttonText: { + color: 'white', + fontWeight: 'bold', + textAlign: 'center', + }, + longText: { + color: 'white', + fontWeight: 'bold', + textAlign: 'center', + fontSize: 16, + }, +}); diff --git a/apps/basic-example/src/RuntimeDecoration.tsx b/apps/basic-example/src/RuntimeDecoration.tsx index 540cf1ef1c..d3baa9eb3c 100644 --- a/apps/basic-example/src/RuntimeDecoration.tsx +++ b/apps/basic-example/src/RuntimeDecoration.tsx @@ -1,190 +1,105 @@ -import React from 'react'; -import { View, StyleSheet, Text, SafeAreaView } from 'react-native'; -import { - GestureHandlerRootView, - ScrollView, - RectButton, -} from 'react-native-gesture-handler'; +import * as React from 'react'; +import { StyleSheet, Text, View } from 'react-native'; +import { Gesture, GestureDetector } from 'react-native-gesture-handler'; + +import { COLORS } from './colors'; +import Animated, { + runOnJS, + useAnimatedStyle, + useSharedValue, +} from 'react-native-reanimated'; +import { useState } from 'react'; + +export function RuntimeChecker({ + runGestureOnJS, +}: { + runGestureOnJS: boolean; +}) { + const pressed = useSharedValue(false); + const active = useSharedValue(false); + const posX = useSharedValue(0); + const posY = useSharedValue(0); + + const start = useSharedValue({ x: 0, y: 0 }); + const [isUIRuntime, setIsUIRuntime] = useState(null); + + const style = useAnimatedStyle(() => { + return { + transform: [ + { translateX: posX.value }, + { translateY: posY.value }, + { scale: pressed.value ? 1.2 : 1 }, + ], + backgroundColor: active.value ? COLORS.KINDA_GREEN : COLORS.KINDA_BLUE, + }; + }); + + const gesture = Gesture.Manual() + .runOnJS(runGestureOnJS) + .onTouchesDown((e) => { + if (!pressed.value) { + pressed.value = true; + start.value = { + x: e.allTouches[0].absoluteX, + y: e.allTouches[0].absoluteY, + }; + } + runOnJS(setIsUIRuntime)(_WORKLET ?? false); + }) + .onTouchesMove((e, state) => { + const dist = Math.sqrt( + Math.pow(e.allTouches[0].absoluteX - start.value.x, 2) + + Math.pow(e.allTouches[0].absoluteY - start.value.y, 2) + ); + + if (active.value) { + posX.value = e.allTouches[0].absoluteX - start.value.x; + posY.value = e.allTouches[0].absoluteY - start.value.y; + } else { + if (dist > 10) { + state.activate(); + start.value = { + x: e.allTouches[0].absoluteX, + y: e.allTouches[0].absoluteY, + }; + } + } + }) + .onTouchesUp((e, state) => { + if (e.allTouches.length === e.changedTouches.length) { + state.end(); + } + }) + .onStart(() => { + active.value = true; + }) + .onFinalize(() => { + pressed.value = false; + active.value = false; + }); -const MyButton = RectButton; - -export default function ComplexUI() { - return ( - - - - - - - - - - - - - - - - - ); -} - -const colors = ['#782AEB', '#38ACDD', '#57B495', '#FF6259', '#FFD61E']; - -function Avatars() { - return ( - - {colors.map((color) => ( - - {color.slice(1, 3)} - - ))} - - ); -} - -function Gallery() { - return ( - - Basic Gallery - - - - - - - - - - ); -} - -function SizeConstraints() { return ( - - Size Constraints - - - Min/Max - - - 1:1 - - - Flex - - + + + {isUIRuntime === null + ? 'Start the gesture to check the runtime' + : `Running on the ${isUIRuntime ? 'UI' : 'JS'} runtime`} + + + + ); } -function FlexboxTests() { +export default function RuntimeDecorationExample() { return ( - - Flexbox Layouts - - - Start - - - Center - - - End - - - - - Wrap 1 - - - Wrap 2 - - - Wrap 3 - - - Wrap 4 - - - - ); -} - -function PositioningTests() { - return ( - - Positioning - - - Z-Index - - - Absolute - - - Relative - - - - ); -} - -function SpacingTests() { - return ( - - Spacing & Overflow - - - Padding - - - Margin - - - Overflow Hidden Test - - - - ); -} - -function VisualEffects() { - return ( - - Visual Effects - - - Shadow - - - Opacity - - - - ); -} - -function ComplexCombinations() { - return ( - - Complex Combinations - - - Complex 1 - - - Complex 2 - - - Complex 3 - - - Complex 4 - - + + + + ); } @@ -192,308 +107,17 @@ function ComplexCombinations() { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: '#f5f5f5', - }, - scrollContent: { - paddingBottom: 50, - }, - paddedContainer: { - padding: 16, - }, - section: { - marginBottom: 30, - }, - sectionTitle: { - fontSize: 18, - fontWeight: 'bold', - marginBottom: 12, - color: '#333', - }, - gap: { - gap: 10, - }, - row: { - flexDirection: 'row', - }, - - // Avatar styles - avatars: { - width: 90, - height: 90, - borderWidth: 2, - borderColor: '#001A72', - borderTopLeftRadius: 30, - borderTopRightRadius: 5, - borderBottomLeftRadius: 5, - borderBottomRightRadius: 30, - marginHorizontal: 4, - alignItems: 'center', - justifyContent: 'center', - }, - avatarLabel: { - color: '#F8F9FF', - fontSize: 24, - fontWeight: 'bold', - }, - - // Gallery styles - fullWidthButton: { - width: '100%', - height: 160, - backgroundColor: '#FF6259', - borderTopRightRadius: 30, - borderTopLeftRadius: 30, - borderWidth: 1, - borderColor: '#000', - }, - leftButton: { - flex: 1, - height: 160, - backgroundColor: '#FFD61E', - borderBottomLeftRadius: 30, - borderWidth: 5, - borderColor: '#000', - }, - rightButton: { - flex: 1, - backgroundColor: '#782AEB', - height: 160, - borderBottomRightRadius: 30, - borderWidth: 8, - borderColor: '#000', - }, - - // Size constraint styles - minMaxButton: { - minWidth: 80, - maxWidth: 120, - minHeight: 40, - maxHeight: 80, - backgroundColor: '#38ACDD', - borderRadius: 10, - justifyContent: 'center', - alignItems: 'center', - }, - aspectRatioButton: { - width: 80, - aspectRatio: 1, - backgroundColor: '#57B495', - borderRadius: 40, - justifyContent: 'center', alignItems: 'center', - }, - flexGrowButton: { - flexGrow: 1, - height: 60, - backgroundColor: '#FF6259', - borderRadius: 15, justifyContent: 'center', - alignItems: 'center', - }, - - // Flexbox styles - flexContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - height: 80, - backgroundColor: '#e0e0e0', - borderRadius: 10, - padding: 10, - marginBottom: 10, - }, - flexStart: { - alignSelf: 'flex-start', - backgroundColor: '#782AEB', - padding: 10, - borderRadius: 5, + gap: 8, }, - flexCenter: { - alignSelf: 'center', - backgroundColor: '#38ACDD', - padding: 10, - borderRadius: 5, - }, - flexEnd: { - alignSelf: 'flex-end', - backgroundColor: '#57B495', - padding: 10, - borderRadius: 5, - }, - flexWrapContainer: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 5, - }, - wrapItem: { - width: '48%', + box: { + width: 50, height: 50, - backgroundColor: '#FFD61E', - borderRadius: 8, - justifyContent: 'center', - alignItems: 'center', - }, - - // Positioning styles - positionContainer: { - height: 80, - backgroundColor: '#e0e0e0', - borderRadius: 10, - position: 'relative', }, - absoluteButton: { - position: 'absolute', - top: 10, - right: 10, - backgroundColor: '#FF6259', - padding: 8, - borderRadius: 5, - }, - relativeButton: { - position: 'relative', - top: 20, - left: 20, - backgroundColor: '#782AEB', - padding: 8, - borderRadius: 5, - }, - zIndexButton: { - position: 'absolute', - top: 10, - left: 10, - zIndex: 10, - backgroundColor: '#57B495', - padding: 8, - borderRadius: 5, - }, - - // Spacing styles - paddingButton: { - flex: 1, - backgroundColor: '#38ACDD', - paddingVertical: 20, - paddingHorizontal: 15, - borderRadius: 10, - justifyContent: 'center', - alignItems: 'center', - }, - marginButton: { - flex: 1, - backgroundColor: '#FFD61E', - margin: 10, - padding: 10, - borderRadius: 10, - justifyContent: 'center', - alignItems: 'center', - }, - overflowButton: { - flex: 1, - height: 60, - backgroundColor: '#782AEB', - borderRadius: 10, - overflow: 'hidden', - justifyContent: 'center', - alignItems: 'center', - }, - - // Visual effect styles - shadowButton: { - flex: 1, - height: 60, - backgroundColor: '#FF6259', - borderRadius: 10, - justifyContent: 'center', - alignItems: 'center', - shadowColor: '#000', - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.3, - shadowRadius: 8, - elevation: 8, - }, - opacityButton: { - flex: 1, - height: 60, - backgroundColor: '#782AEB', - borderRadius: 10, - justifyContent: 'center', - alignItems: 'center', - opacity: 0.7, - }, - - // Complex combination styles - complexGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 10, - }, - complexButton1: { - width: '48%', - height: 100, - backgroundColor: '#FF6259', - borderRadius: 20, - borderWidth: 2, - borderColor: '#782AEB', - justifyContent: 'center', - alignItems: 'center', - shadowColor: '#000', - shadowOffset: { width: 2, height: 2 }, - shadowOpacity: 0.2, - shadowRadius: 4, - elevation: 4, - marginBottom: 5, - }, - complexButton2: { - width: '48%', - minHeight: 80, - maxHeight: 120, - backgroundColor: '#38ACDD', - borderTopLeftRadius: 30, - borderBottomRightRadius: 30, - paddingVertical: 15, - paddingHorizontal: 10, - justifyContent: 'center', - alignItems: 'center', - overflow: 'hidden', - }, - complexButton3: { - width: '48%', - aspectRatio: 1.5, - backgroundColor: '#57B495', - borderRadius: 15, - borderWidth: 4, - borderColor: '#FFD61E', - justifyContent: 'center', - alignItems: 'center', - opacity: 0.9, - marginTop: 10, - }, - complexButton4: { - width: '48%', - height: 80, - backgroundColor: '#FFD61E', - borderRadius: 10, - borderWidth: 1, - borderColor: '#FF6259', - justifyContent: 'center', - alignItems: 'center', - shadowColor: '#782AEB', - shadowOffset: { width: 0, height: 6 }, - shadowOpacity: 0.4, - shadowRadius: 10, - elevation: 10, - marginTop: 10, - }, - - // Text styles - buttonText: { - color: 'white', - fontWeight: 'bold', - textAlign: 'center', - }, - longText: { - color: 'white', - fontWeight: 'bold', - textAlign: 'center', - fontSize: 16, + separator: { + width: '100%', + height: 1, + backgroundColor: 'black', }, }); From d735767bb47f5fccefcfde285aa4884d7bd49f10 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 24 Oct 2025 15:08:12 +0200 Subject: [PATCH 08/12] Fix transform handling --- apps/basic-example/src/ContentsButton.tsx | 25 +++++++++++++++++++ .../src/components/GestureHandlerButton.tsx | 13 +++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/apps/basic-example/src/ContentsButton.tsx b/apps/basic-example/src/ContentsButton.tsx index 540cf1ef1c..50600d2ce8 100644 --- a/apps/basic-example/src/ContentsButton.tsx +++ b/apps/basic-example/src/ContentsButton.tsx @@ -22,6 +22,7 @@ export default function ComplexUI() { + @@ -189,6 +190,19 @@ function ComplexCombinations() { ); } +function Transforms() { + return ( + + Transform + + + Transform + + + + ); +} + const styles = StyleSheet.create({ container: { flex: 1, @@ -484,6 +498,17 @@ const styles = StyleSheet.create({ marginTop: 10, }, + // Transform styles + transformButton: { + width: '48%', + height: 100, + backgroundColor: '#38ACDD', + borderRadius: 15, + justifyContent: 'center', + alignItems: 'center', + transform: [{ translateX: 100 }, { rotate: '15deg' }, { scale: 1.1 }], + }, + // Text styles buttonText: { color: 'white', diff --git a/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx b/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx index 51fc9bdf4c..aa7f9ef7e7 100644 --- a/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx +++ b/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx @@ -62,6 +62,8 @@ export default function GestureHandlerButton({ start, end, overflow, + transform, + transformOrigin, zIndex, // Visual properties @@ -123,9 +125,18 @@ export default function GestureHandlerButton({ [flattenedStyle] ); + const wrapperStyle = useMemo( + () => ({ + zIndex, + transform, + transformOrigin, + }), + [zIndex, transform, transformOrigin] + ); + return ( + style={[styles.contents, wrapperStyle]}> Date: Fri, 24 Oct 2025 15:41:51 +0200 Subject: [PATCH 09/12] Simplify setup --- .../RNGestureHandlerButtonWrapperView.kt | 6 ----- ...NGestureHandlerButtonWrapperViewManager.kt | 23 ++++++++----------- .../apple/RNGestureHandlerButtonWrapper.mm | 2 -- ...NGestureHandlerButtonWrapperShadowNode.cpp | 3 --- .../src/components/GestureHandlerButton.tsx | 15 +----------- 5 files changed, 11 insertions(+), 38 deletions(-) delete mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperView.kt diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperView.kt deleted file mode 100644 index f6b899968d..0000000000 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperView.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.swmansion.gesturehandler.react - -import android.content.Context -import com.facebook.react.views.view.ReactViewGroup - -class RNGestureHandlerButtonWrapperView(context: Context) : ReactViewGroup(context) diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperViewManager.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperViewManager.kt index 617d7b0877..7137243bd7 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperViewManager.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonWrapperViewManager.kt @@ -6,26 +6,23 @@ import com.facebook.react.uimanager.ViewGroupManager import com.facebook.react.uimanager.ViewManagerDelegate import com.facebook.react.viewmanagers.RNGestureHandlerButtonWrapperManagerDelegate import com.facebook.react.viewmanagers.RNGestureHandlerButtonWrapperManagerInterface +import com.facebook.react.views.view.ReactViewGroup @ReactModule(name = RNGestureHandlerButtonWrapperViewManager.REACT_CLASS) class RNGestureHandlerButtonWrapperViewManager : - ViewGroupManager(), - RNGestureHandlerButtonWrapperManagerInterface { - private val mDelegate: ViewManagerDelegate + ViewGroupManager(), + RNGestureHandlerButtonWrapperManagerInterface { + private val mDelegate: ViewManagerDelegate = + RNGestureHandlerButtonWrapperManagerDelegate< + ReactViewGroup, + RNGestureHandlerButtonWrapperViewManager, + >(this) - init { - mDelegate = - RNGestureHandlerButtonWrapperManagerDelegate< - RNGestureHandlerButtonWrapperView, - RNGestureHandlerButtonWrapperViewManager, - >(this) - } - - override fun getDelegate(): ViewManagerDelegate = mDelegate + override fun getDelegate(): ViewManagerDelegate = mDelegate override fun getName() = REACT_CLASS - override fun createViewInstance(reactContext: ThemedReactContext) = RNGestureHandlerButtonWrapperView(reactContext) + override fun createViewInstance(reactContext: ThemedReactContext) = ReactViewGroup(reactContext) companion object { const val REACT_CLASS = "RNGestureHandlerButtonWrapper" diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.mm index 308b69ac76..526c7743d6 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerButtonWrapper.mm @@ -9,8 +9,6 @@ #import #import -#include - @interface RNGestureHandlerButtonWrapper () @end diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp index 3fa4911dae..66fc9c6185 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp @@ -19,9 +19,6 @@ extern const char RNGestureHandlerButtonWrapperComponentName[] = "RNGestureHandlerButtonWrapper"; void RNGestureHandlerButtonWrapperShadowNode::initialize() { - // Disable forcing view flattening - ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView); - // When the button wrapper is cloned and has a child node, the child node // should be cloned as well to ensure it is mutable. if (!getChildren().empty()) { diff --git a/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx b/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx index aa7f9ef7e7..a3e1a6026a 100644 --- a/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx +++ b/packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx @@ -62,9 +62,6 @@ export default function GestureHandlerButton({ start, end, overflow, - transform, - transformOrigin, - zIndex, // Visual properties ...restStyle @@ -125,18 +122,8 @@ export default function GestureHandlerButton({ [flattenedStyle] ); - const wrapperStyle = useMemo( - () => ({ - zIndex, - transform, - transformOrigin, - }), - [zIndex, transform, transformOrigin] - ); - return ( - + Date: Tue, 28 Oct 2025 09:20:26 +0100 Subject: [PATCH 10/12] Remove comment headers --- .../RNGestureHandlerButtonWrapperShadowNode.cpp | 10 ---------- .../RNGestureHandlerButtonWrapperShadowNode.h | 11 ----------- 2 files changed, 21 deletions(-) diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp index 66fc9c6185..a36d5699c0 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp @@ -1,14 +1,4 @@ -/** - * This code was generated by - * [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). - * - * Do not edit this file as changes may cause incorrect behavior and will be - * lost once the code is regenerated. - * - * @generated by codegen project: GenerateShadowNodeCpp.js - */ - #include #include "RNGestureHandlerButtonWrapperShadowNode.h" diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h index 1c53c1491b..79c87ea6bd 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.h @@ -1,14 +1,3 @@ - -/** - * This code was generated by - * [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). - * - * Do not edit this file as changes may cause incorrect behavior and will be - * lost once the code is regenerated. - * - * @generated by codegen project: GenerateShadowNodeH.js - */ - #pragma once #include From 3d91d6e935963528bdb6332d5b780bcb5f364579 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 28 Oct 2025 11:22:37 +0100 Subject: [PATCH 11/12] Fix shadow node layout --- .../RNGestureHandlerButtonWrapperShadowNode.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp index a36d5699c0..9b327dad51 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperShadowNode.cpp @@ -68,12 +68,10 @@ void RNGestureHandlerButtonWrapperShadowNode::layout( // TODO: figure out the correct way to setup metrics between button wrapper // and the child auto metrics = grandChild->getLayoutMetrics(); - setLayoutMetrics(metrics); + mutableChild->setLayoutMetrics(metrics); auto metricsNoOrigin = grandChild->getLayoutMetrics(); metricsNoOrigin.frame.origin = Point{}; - - mutableChild->setLayoutMetrics(metricsNoOrigin); mutableGrandChild->setLayoutMetrics(metricsNoOrigin); } From 9def2c09c8f39a3e26d747cb9d6d058183aa2e0f Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 28 Oct 2025 11:34:31 +0100 Subject: [PATCH 12/12] Rename component --- apps/basic-example/src/ContentsButton.tsx | 106 +++++++++++----------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/apps/basic-example/src/ContentsButton.tsx b/apps/basic-example/src/ContentsButton.tsx index 50600d2ce8..23906db5ad 100644 --- a/apps/basic-example/src/ContentsButton.tsx +++ b/apps/basic-example/src/ContentsButton.tsx @@ -6,8 +6,6 @@ import { RectButton, } from 'react-native-gesture-handler'; -const MyButton = RectButton; - export default function ComplexUI() { return ( @@ -36,11 +34,11 @@ function Avatars() { return ( {colors.map((color) => ( - {color.slice(1, 3)} - + ))} ); @@ -51,12 +49,12 @@ function Gallery() { Basic Gallery - + - - + + - + ); @@ -67,15 +65,15 @@ function SizeConstraints() { Size Constraints - + Min/Max - - + + 1:1 - - + + Flex - + ); @@ -86,29 +84,29 @@ function FlexboxTests() { Flexbox Layouts - + Start - - + + Center - - + + End - + - + Wrap 1 - - + + Wrap 2 - - + + Wrap 3 - - + + Wrap 4 - + ); @@ -119,15 +117,15 @@ function PositioningTests() { Positioning - + Z-Index - - + + Absolute - - + + Relative - + ); @@ -138,15 +136,15 @@ function SpacingTests() { Spacing & Overflow - + Padding - - + + Margin - - + + Overflow Hidden Test - + ); @@ -157,12 +155,12 @@ function VisualEffects() { Visual Effects - + Shadow - - + + Opacity - + ); @@ -173,18 +171,18 @@ function ComplexCombinations() { Complex Combinations - + Complex 1 - - + + Complex 2 - - + + Complex 3 - - + + Complex 4 - + ); @@ -195,9 +193,9 @@ function Transforms() { Transform - + Transform - + );