From 389ec1177921a09a2eb657e634800e12e9bfd5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Fri, 31 Jan 2025 14:11:18 +0100 Subject: [PATCH 01/13] fix ripple color on Pressable --- src/components/Pressable/Pressable.tsx | 9 +++++++-- src/utils.ts | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/Pressable/Pressable.tsx b/src/components/Pressable/Pressable.tsx index 9223a18cc1..9736012dfe 100644 --- a/src/components/Pressable/Pressable.tsx +++ b/src/components/Pressable/Pressable.tsx @@ -20,7 +20,7 @@ import { } from './utils'; import { PressabilityDebugView } from '../../handlers/PressabilityDebugView'; import { GestureTouchEvent } from '../../handlers/gestureHandlerCommon'; -import { INT32_MAX } from '../../utils'; +import { INT32_MAX, isFabricUsed } from '../../utils'; const DEFAULT_LONG_PRESS_DURATION = 500; @@ -380,6 +380,11 @@ export default function Pressable(props: PressableProps) { ? children({ pressed: pressedState }) : children; + const fabricRippleColor = android_ripple?.color ?? defaultRippleColor; + const rippleColor = isFabricUsed() + ? fabricRippleColor + : processColor(fabricRippleColor); + return ( {childrenProp} diff --git a/src/utils.ts b/src/utils.ts index 8d7284a443..f33286044c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -36,9 +36,17 @@ export function hasProperty(object: object, key: string) { export function isJestEnv(): boolean { // @ts-ignore Do not use `@types/node` because it will prioritise Node types over RN types which breaks the types (ex. setTimeout) in React Native projects. + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access return hasProperty(global, 'process') && !!process.env.JEST_WORKER_ID; } +export function isFabricUsed(): boolean { + return ( + // @ts-ignore global is untyped, but always present, nativeFabricUIManager will be present when running on fabric + hasProperty(global, 'nativeFabricUIManager') && !!nativeFabricUIManager + ); +} + export function tagMessage(msg: string) { return `[react-native-gesture-handler] ${msg}`; } From 3e9ac807abeaffbd6a624dfe000033b34f08f96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Fri, 31 Jan 2025 14:31:43 +0100 Subject: [PATCH 02/13] remove duplicate function --- src/components/Pressable/Pressable.tsx | 4 ++-- src/utils.ts | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/components/Pressable/Pressable.tsx b/src/components/Pressable/Pressable.tsx index 9736012dfe..27961d7f2c 100644 --- a/src/components/Pressable/Pressable.tsx +++ b/src/components/Pressable/Pressable.tsx @@ -20,7 +20,7 @@ import { } from './utils'; import { PressabilityDebugView } from '../../handlers/PressabilityDebugView'; import { GestureTouchEvent } from '../../handlers/gestureHandlerCommon'; -import { INT32_MAX, isFabricUsed } from '../../utils'; +import { INT32_MAX, isFabric } from '../../utils'; const DEFAULT_LONG_PRESS_DURATION = 500; @@ -381,7 +381,7 @@ export default function Pressable(props: PressableProps) { : children; const fabricRippleColor = android_ripple?.color ?? defaultRippleColor; - const rippleColor = isFabricUsed() + const rippleColor = isFabric() ? fabricRippleColor : processColor(fabricRippleColor); diff --git a/src/utils.ts b/src/utils.ts index f33286044c..c49566e1ea 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -40,13 +40,6 @@ export function isJestEnv(): boolean { return hasProperty(global, 'process') && !!process.env.JEST_WORKER_ID; } -export function isFabricUsed(): boolean { - return ( - // @ts-ignore global is untyped, but always present, nativeFabricUIManager will be present when running on fabric - hasProperty(global, 'nativeFabricUIManager') && !!nativeFabricUIManager - ); -} - export function tagMessage(msg: string) { return `[react-native-gesture-handler] ${msg}`; } From d42cefd7ea26a0d83b7a1a3e73cf51f799f442bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Fri, 31 Jan 2025 14:32:46 +0100 Subject: [PATCH 03/13] add fabric checking to base button --- src/components/GestureButtons.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/GestureButtons.tsx b/src/components/GestureButtons.tsx index 8737e903f2..570ccad03c 100644 --- a/src/components/GestureButtons.tsx +++ b/src/components/GestureButtons.tsx @@ -18,6 +18,7 @@ import type { BorderlessButtonWithRefProps, BorderlessButtonProps, } from './GestureButtonsProps'; +import { isFabric } from '../utils'; export const RawButton = createNativeWrapper(GestureHandlerButton, { shouldCancelWhenOutside: false, @@ -122,10 +123,14 @@ class InnerBaseButton extends React.Component { render() { const { rippleColor, style, ...rest } = this.props; + const processedRippleColor = isFabric() + ? rippleColor + : processColor(rippleColor ?? undefined); + return ( Date: Fri, 31 Jan 2025 14:32:55 +0100 Subject: [PATCH 04/13] add proper typing to RawButton --- src/components/GestureButtonsProps.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/GestureButtonsProps.ts b/src/components/GestureButtonsProps.ts index e5075a17da..0bd7f2cc63 100644 --- a/src/components/GestureButtonsProps.ts +++ b/src/components/GestureButtonsProps.ts @@ -1,5 +1,10 @@ import * as React from 'react'; -import { AccessibilityProps, StyleProp, ViewStyle } from 'react-native'; +import { + AccessibilityProps, + ColorValue, + StyleProp, + ViewStyle, +} from 'react-native'; import type { NativeViewGestureHandlerProps } from '../handlers/NativeViewGestureHandler'; export interface RawButtonProps @@ -16,7 +21,7 @@ export interface RawButtonProps * * Defines color of native ripple animation used since API level 21. */ - rippleColor?: any; // it was present in BaseButtonProps before but is used here in code + rippleColor?: number | ColorValue | null; /** * Android only. From f7d895d69be9cb39ea712168a62745a42d393b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Fri, 31 Jan 2025 14:35:40 +0100 Subject: [PATCH 05/13] adjust name --- src/components/Pressable/Pressable.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Pressable/Pressable.tsx b/src/components/Pressable/Pressable.tsx index 27961d7f2c..13c9f33475 100644 --- a/src/components/Pressable/Pressable.tsx +++ b/src/components/Pressable/Pressable.tsx @@ -380,10 +380,10 @@ export default function Pressable(props: PressableProps) { ? children({ pressed: pressedState }) : children; - const fabricRippleColor = android_ripple?.color ?? defaultRippleColor; + const unprocessedRippleColor = android_ripple?.color ?? defaultRippleColor; const rippleColor = isFabric() - ? fabricRippleColor - : processColor(fabricRippleColor); + ? unprocessedRippleColor + : processColor(unprocessedRippleColor); return ( From 4e8dce8ab62ddfe15d71a72ff9610c2f3cefc38c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Fri, 31 Jan 2025 14:36:44 +0100 Subject: [PATCH 06/13] remove unnecessary lint ignore --- src/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index c49566e1ea..8d7284a443 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -36,7 +36,6 @@ export function hasProperty(object: object, key: string) { export function isJestEnv(): boolean { // @ts-ignore Do not use `@types/node` because it will prioritise Node types over RN types which breaks the types (ex. setTimeout) in React Native projects. - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access return hasProperty(global, 'process') && !!process.env.JEST_WORKER_ID; } From 286ead3784661bc108650bcf35e87b4d19acd2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Fri, 31 Jan 2025 14:54:27 +0100 Subject: [PATCH 07/13] memoize ripple color and default ripple color --- src/components/Pressable/Pressable.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/Pressable/Pressable.tsx b/src/components/Pressable/Pressable.tsx index 13c9f33475..02d0d74310 100644 --- a/src/components/Pressable/Pressable.tsx +++ b/src/components/Pressable/Pressable.tsx @@ -366,8 +366,6 @@ export default function Pressable(props: PressableProps) { const gesture = Gesture.Simultaneous(...gestures); - const defaultRippleColor = android_ripple ? undefined : 'transparent'; - // `cursor: 'pointer'` on `RNButton` crashes iOS const pointerStyle: StyleProp = Platform.OS === 'web' ? { cursor: 'pointer' } : {}; @@ -380,10 +378,17 @@ export default function Pressable(props: PressableProps) { ? children({ pressed: pressedState }) : children; - const unprocessedRippleColor = android_ripple?.color ?? defaultRippleColor; - const rippleColor = isFabric() - ? unprocessedRippleColor - : processColor(unprocessedRippleColor); + const defaultRippleColor = useMemo( + () => (android_ripple ? undefined : 'transparent'), + [android_ripple] + ); + + const rippleColor = useMemo(() => { + const unprocessedRippleColor = android_ripple?.color ?? defaultRippleColor; + return isFabric() + ? unprocessedRippleColor + : processColor(unprocessedRippleColor); + }, [android_ripple?.color, defaultRippleColor]); return ( From a9d203156e759bf5fd8af644f0609b5821a5236a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Fri, 31 Jan 2025 15:06:41 +0100 Subject: [PATCH 08/13] merge both useMemo --- src/components/Pressable/Pressable.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/Pressable/Pressable.tsx b/src/components/Pressable/Pressable.tsx index 02d0d74310..c10a13559f 100644 --- a/src/components/Pressable/Pressable.tsx +++ b/src/components/Pressable/Pressable.tsx @@ -378,17 +378,13 @@ export default function Pressable(props: PressableProps) { ? children({ pressed: pressedState }) : children; - const defaultRippleColor = useMemo( - () => (android_ripple ? undefined : 'transparent'), - [android_ripple] - ); - const rippleColor = useMemo(() => { + const defaultRippleColor = android_ripple ? undefined : 'transparent'; const unprocessedRippleColor = android_ripple?.color ?? defaultRippleColor; return isFabric() ? unprocessedRippleColor : processColor(unprocessedRippleColor); - }, [android_ripple?.color, defaultRippleColor]); + }, [android_ripple]); return ( From ff48533eacbf8641c7005744fbf12e6d89dc01a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Mon, 3 Feb 2025 13:10:29 +0100 Subject: [PATCH 09/13] memo isFabric --- src/components/Pressable/Pressable.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Pressable/Pressable.tsx b/src/components/Pressable/Pressable.tsx index 1819110704..36cd6782e5 100644 --- a/src/components/Pressable/Pressable.tsx +++ b/src/components/Pressable/Pressable.tsx @@ -379,13 +379,15 @@ export default function Pressable(props: PressableProps) { ? children({ pressed: pressedState }) : children; + const memoIsFabric = useCallback(() => isFabric, []); + const rippleColor = useMemo(() => { const defaultRippleColor = android_ripple ? undefined : 'transparent'; const unprocessedRippleColor = android_ripple?.color ?? defaultRippleColor; - return isFabric() + return memoIsFabric() ? unprocessedRippleColor : processColor(unprocessedRippleColor); - }, [android_ripple]); + }, [android_ripple, memoIsFabric]); return ( From 2513b9e5bc1a71d974d4b0c5fbd52d2985676013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Mon, 3 Feb 2025 14:14:36 +0100 Subject: [PATCH 10/13] fix working in `BaseButton` --- src/components/GestureButtons.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/GestureButtons.tsx b/src/components/GestureButtons.tsx index 570ccad03c..b9b3039728 100644 --- a/src/components/GestureButtons.tsx +++ b/src/components/GestureButtons.tsx @@ -121,16 +121,16 @@ class InnerBaseButton extends React.Component { }; render() { - const { rippleColor, style, ...rest } = this.props; + const { rippleColor: unprocessedRippleColor, style, ...rest } = this.props; - const processedRippleColor = isFabric() - ? rippleColor - : processColor(rippleColor ?? undefined); + const rippleColor = isFabric() + ? unprocessedRippleColor + : processColor(unprocessedRippleColor ?? undefined); return ( Date: Mon, 3 Feb 2025 14:15:34 +0100 Subject: [PATCH 11/13] change function to value --- src/components/Pressable/Pressable.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Pressable/Pressable.tsx b/src/components/Pressable/Pressable.tsx index 36cd6782e5..5ecc08ec74 100644 --- a/src/components/Pressable/Pressable.tsx +++ b/src/components/Pressable/Pressable.tsx @@ -379,15 +379,15 @@ export default function Pressable(props: PressableProps) { ? children({ pressed: pressedState }) : children; - const memoIsFabric = useCallback(() => isFabric, []); + const isFabricValue = useMemo(() => isFabric(), []); const rippleColor = useMemo(() => { const defaultRippleColor = android_ripple ? undefined : 'transparent'; const unprocessedRippleColor = android_ripple?.color ?? defaultRippleColor; - return memoIsFabric() + return isFabricValue ? unprocessedRippleColor : processColor(unprocessedRippleColor); - }, [android_ripple, memoIsFabric]); + }, [android_ripple, isFabricValue]); return ( From d62793c45821c5b5a67cfc022b374ede03885afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Mon, 3 Feb 2025 14:50:06 +0100 Subject: [PATCH 12/13] add fabric value caching to BaseButton --- src/components/GestureButtons.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/GestureButtons.tsx b/src/components/GestureButtons.tsx index b9b3039728..965ffb21f8 100644 --- a/src/components/GestureButtons.tsx +++ b/src/components/GestureButtons.tsx @@ -25,6 +25,8 @@ export const RawButton = createNativeWrapper(GestureHandlerButton, { shouldActivateOnStart: false, }); +let IS_FABRIC: null | boolean = null; + class InnerBaseButton extends React.Component { static defaultProps = { delayLongPress: 600, @@ -123,7 +125,11 @@ class InnerBaseButton extends React.Component { render() { const { rippleColor: unprocessedRippleColor, style, ...rest } = this.props; - const rippleColor = isFabric() + if (IS_FABRIC === null) { + IS_FABRIC = isFabric(); + } + + const rippleColor = IS_FABRIC ? unprocessedRippleColor : processColor(unprocessedRippleColor ?? undefined); From f8b2de56030319877a74b96d5260cd235d867aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20=C5=81=C4=85tka?= Date: Mon, 3 Feb 2025 16:11:30 +0100 Subject: [PATCH 13/13] simplify IS_FABRIC logic in Pressable --- src/components/Pressable/Pressable.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/Pressable/Pressable.tsx b/src/components/Pressable/Pressable.tsx index 5ecc08ec74..b6a7c960cb 100644 --- a/src/components/Pressable/Pressable.tsx +++ b/src/components/Pressable/Pressable.tsx @@ -25,6 +25,8 @@ import { INT32_MAX, isFabric, isTestEnv } from '../../utils'; const DEFAULT_LONG_PRESS_DURATION = 500; const IS_TEST_ENV = isTestEnv(); +let IS_FABRIC: null | boolean = null; + export default function Pressable(props: PressableProps) { const { testOnly_pressed, @@ -379,15 +381,17 @@ export default function Pressable(props: PressableProps) { ? children({ pressed: pressedState }) : children; - const isFabricValue = useMemo(() => isFabric(), []); - const rippleColor = useMemo(() => { + if (IS_FABRIC === null) { + IS_FABRIC = isFabric(); + } + const defaultRippleColor = android_ripple ? undefined : 'transparent'; const unprocessedRippleColor = android_ripple?.color ?? defaultRippleColor; - return isFabricValue + return IS_FABRIC ? unprocessedRippleColor : processColor(unprocessedRippleColor); - }, [android_ripple, isFabricValue]); + }, [android_ripple]); return (