diff --git a/packages/docs-gesture-handler/docs/components/buttons.mdx b/packages/docs-gesture-handler/docs/components/buttons.mdx index 720556a0ad..55e5cb9485 100644 --- a/packages/docs-gesture-handler/docs/components/buttons.mdx +++ b/packages/docs-gesture-handler/docs/components/buttons.mdx @@ -9,6 +9,10 @@ import GifGallery from '@site/components/GifGallery'; import HeaderWithBadges from '@site/src/components/HeaderWithBadges'; +:::danger +Button components described in this section are deprecated and will be removed in the future. Please use [`Touchable`](/docs/components/touchable) instead. +::: + @@ -37,7 +41,7 @@ The most basic button component does not provide any feedback and lacks props su exclusive?: boolean; ``` -Defines if more than one button could be pressed simultaneously. By default set to `true`. +Defines whether pressing this button prevents other buttons exported by Gesture Handler from being pressed. By default set to `true`. ### rippleColor @@ -194,7 +198,7 @@ If you wish to get specific information about platforms design patterns, visit [ This library allows the use of native components with native feedback in adequate situations. -If you do not wish to implement a custom design approach, [`RectButton`](#rectbutton) and [`BorderlessButton`](#borderlessbutton) seem to be absolutely enough and there's no need to use anything else. In all the remaining cases, you can always rely on [`BaseButton`](#basebutton) which is a superclass for the other button classes and can be used as a generic `Touchable` replacement that can be customized to your needs. +If you do not wish to implement a custom design approach, [`RectButton`](#rectbutton) and [`BorderlessButton`](#borderlessbutton) seem to be absolutely enough and there's no need to use anything else. In all the remaining cases, you can always rely on [`BaseButton`](#basebutton) which is a superclass for the other button classes and can be used as a generic React Native `Touchable` replacement that can be customized to your needs. Below we list some of the common usecases for button components to be used along with the type of button that should be used according to the platform specific design guidelines. diff --git a/packages/docs-gesture-handler/docs/components/touchable.mdx b/packages/docs-gesture-handler/docs/components/touchable.mdx new file mode 100644 index 0000000000..cd6b7502b8 --- /dev/null +++ b/packages/docs-gesture-handler/docs/components/touchable.mdx @@ -0,0 +1,326 @@ +--- +id: touchable +title: Touchable +sidebar_label: Touchable +--- + +import HeaderWithBadges from '@site/src/components/HeaderWithBadges'; + +:::note +This section refers to new `Touchable` component, meant to replace both buttons and touchables. If you are looking for documentation for the deprecated touchable components, check out the [Legacy Touchables](/docs/components/legacy-touchables) section. +::: + +`Touchable` is a versatile new component introduced in Gesture Handler 3 to supersede previous button implementations. Designed for maximum flexibility, it provides a highly customizable interface for native touch handling while ensuring consistent behavior across platforms. + +`Touchable` provides a simple interface for the common animations like opacity, underlay, and scale, implemented entirely on the platform. On Android, it also exposes the native ripple effect on press (turned off by default). + +If the provided animations are not sufficient, it's possible to use `Touchable` to create fully custom interactions using either [Reanimated](https://docs.swmansion.com/react-native-reanimated/) or [Animated API](https://reactnative.dev/docs/animated). + +## Replacing old buttons + +If you were using `RectButton` or `BorderlessButton` in your app, you should replace them with `Touchable`. Check out the full code in the [example](#example) section below. + +### RectButton + +To replace `RectButton` with `Touchable`, simply add `activeUnderlayOpacity={0.105}` to your `Touchable`. This will animate the underlay when the button is pressed. + +```tsx + +``` + +### BorderlessButton + +Replacing `BorderlessButton` with `Touchable` is as easy as replacing `RectButton`. Just add `activeOpacity={0.3}` to your `Touchable`. This will animate the whole component when the button is pressed. + +```tsx + +``` + +## Migrating from legacy Touchable variants + +If you were using the specialized touchable components (`TouchableOpacity`, `TouchableHighlight`, `TouchableWithoutFeedback`, or `TouchableNativeFeedback`), you can replicate their behavior with the unified `Touchable` component. + +### TouchableOpacity + +To replace `TouchableOpacity`, add `activeOpacity={0.2}`. + +```tsx + +``` + +### TouchableHighlight + +To replace `TouchableHighlight`, add `activeUnderlayOpacity={1}`. + +```tsx + +``` + +### TouchableWithoutFeedback + +To replace `TouchableWithoutFeedback`, use a plain `Touchable`. + +```tsx + +``` + +### TouchableNativeFeedback + +To replicate `TouchableNativeFeedback` behavior, use the [`androidRipple`](#androidripple) prop. Make sure to set `foreground={true}`. + +```tsx + +``` + +## Example + +In this example we will demonstrate how to recreate `RectButton` and `BorderlessButton` effects using the `Touchable` component. + + + { + console.log('BaseButton built with Touchable'); + }} + style={[styles.button, { backgroundColor: '#7d63d9' }]}> + BaseButton + + + { + console.log('RectButton built with Touchable'); + }} + style={[styles.button, { backgroundColor: '#4f9a84' }]} + activeUnderlayOpacity={0.105}> + RectButton + + + { + console.log('BorderlessButton built with Touchable'); + }} + style={[styles.button, { backgroundColor: '#5f97c8' }]} + activeOpacity={0.3}> + BorderlessButton + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + + gap: 20, + }, + button: { + width: 200, + height: 70, + borderRadius: 15, + alignItems: 'center', + justifyContent: 'center', + }, + buttonText: { + color: 'white', + fontSize: 14, + fontWeight: '600', + }, +}); +`}/> + + +## Properties + +### activeOpacity + +```ts +activeOpacity?: number; +``` + +Defines the opacity of the whole component when the button is active. + +### defaultOpacity + +```ts +defaultOpacity?: number; +``` + +Defines the opacity of the whole component when the button is active. By default set to `1`. + +### activeUnderlayOpacity + +```ts +activeUnderlayOpacity?: number; +``` + +Defines the opacity of the underlay when the button is active. By default set to `0`. + +### defaultUnderlayOpacity + +```ts +defaultUnderlayOpacity?: number; +``` + +Defines the initial opacity of underlay when the button is inactive. By default set to `0`. + +### underlayColor + +```ts +underlayColor?: string; +``` + +Background color of the underlay. This only takes effect when `activeUnderlayOpacity` or `defaultUnderlayOpacity` is set. + +### exclusive + +```ts +exclusive?: boolean; +``` + +Defines whether pressing this button prevents other buttons exported by Gesture Handler from being pressed. By default set to `true`. + + +### touchSoundDisabled + + +```ts +touchSoundDisabled?: boolean; +``` + +If set to `true`, the system will not play a sound when the button is pressed. + +### onPressIn + +) => void; + +type GestureEvent = { + handlerTag: number; + numberOfPointers: number; + pointerType: PointerType; + pointerInside: boolean; +} + +enum PointerType { + TOUCH, + STYLUS, + MOUSE, + KEY, + OTHER, +} +`}/> + +Triggered when the button gets pressed (analogous to `onPressIn` in `Pressable` from RN core). + +### onPressOut + +) => void; + +type GestureEvent = { + handlerTag: number; + numberOfPointers: number; + pointerType: PointerType; + pointerInside: boolean; +} + +enum PointerType { + TOUCH, + STYLUS, + MOUSE, + KEY, + OTHER, +} +`}/> + +Triggered when the button gets released or the pointer moves outside of the button area (analogous to `onPressOut` in `Pressable` from RN core). + +### onPress + +```ts +onPress?: (pointerInside: boolean) => void; +``` + +Triggered when the button gets pressed (analogous to `onPress` in `Pressable` from RN core). + +### onLongPress + +```ts +onLongPress?: () => void; +``` + +Triggered when the button gets pressed for at least [`delayLongPress`](#delaylongpress) milliseconds. + + +### onActiveStateChange + +```ts +onActiveStateChange?: (active: boolean) => void; +``` + +Triggered when the button transitions between active and inactive states. It passes the current active state as a boolean variable to the method as the first parameter. + +### delayLongPress + +```ts +delayLongPress?: number; +``` + +Defines the delay, in milliseconds, after which the [`onLongPress`](#onlongpress) callback gets called. By default set to `600`. + + + +### androidRipple + + + + +Configuration for the ripple effect on Android. If not provided, the ripple effect will be disabled. If `{}` is provided, the ripple effect will be enabled with default configuration. diff --git a/packages/docs-gesture-handler/docs/components/touchables.md b/packages/docs-gesture-handler/docs/components/touchables.md index 9271b6738f..3c019cf468 100644 --- a/packages/docs-gesture-handler/docs/components/touchables.md +++ b/packages/docs-gesture-handler/docs/components/touchables.md @@ -1,11 +1,11 @@ --- -id: touchables -title: Touchables -sidebar_label: Touchables +id: legacy-touchables +title: Legacy Touchables +sidebar_label: Legacy Touchables --- :::warning -Touchables will be removed in the future version of Gesture Handler. Use [`Pressable`](/docs/components/pressable) instead. +Touchables will be removed in the future version of Gesture Handler. Use [`Touchable`](/docs/components/touchable) instead. ::: Gesture Handler library provides an implementation of RN's touchable components that are based on [native buttons](buttons.mdx) and do not rely on the JS responder system utilized by RN. Our touchable implementation follows the same API and aims to be a drop-in replacement for touchables available in React Native. diff --git a/packages/docs-gesture-handler/docs/guides/troubleshooting.mdx b/packages/docs-gesture-handler/docs/guides/troubleshooting.mdx index 959cfa6367..85b0b27c8e 100644 --- a/packages/docs-gesture-handler/docs/guides/troubleshooting.mdx +++ b/packages/docs-gesture-handler/docs/guides/troubleshooting.mdx @@ -32,7 +32,7 @@ This library is maintained by a very small team. Please keep this in mind when r - Changing `enabled` prop during a gesture has no effect, only when a gesture starts (that is, a finger touches the screen) the `enabled` prop is taken into consideration to decide whether to extract (or not) the gesture and provide it with stream of events to analyze. - `Native` gesture may not conform to the standard state flow due to platform specific workarounds to incorporate native views into RNGH. -- Keep in mind that [`Touchables`](/docs/components/touchables) from RNGH are rendering two additional views that may need to be styled separately to achieve desired effect (`style` and `containerStyle` props). +- Keep in mind that [Legacy `Touchables`](/docs/components/legacy-touchables) from RNGH are rendering two additional views that may need to be styled separately to achieve desired effect (`style` and `containerStyle` props). - In order for the [gesture composition](/docs/fundamentals/gesture-composition) to work, all composed gestures must be attached to the same [`GestureHandlerRootView`](/docs/fundamentals/root-view). ## Multiple instances of Gesture Handler were detected diff --git a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx index 7e83c0eee8..2055dd4016 100644 --- a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx +++ b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx @@ -246,7 +246,17 @@ code2={ ### Buttons -The implementation of buttons has been updated, resolving most button-related issues. They have also been internally rewritten to utilize the new hook API. The original button components are still accessible but have been renamed with the prefix `Legacy`, e.g., `RectButton` is now available as `LegacyRectButton`. +RNGH3 introduces the [`Touchable`](/docs/components/touchable) component — a flexible, unified replacement for all previous button types. While `Touchable` shares the same logic as our standard buttons, it offers a more customizable API. + +To help you migrate, here is the current state of our button components: + +- [`Touchable`](/docs/components/touchable) - The recommended component. Can be used to replicate both `RectButton` and `BorderlessButton` effects by adjusting the `activeOpacity` and `activeUnderlayOpacity` props. + - To replace `RectButton`, add `activeUnderlayOpacity={0.105}`. + - To replace `BorderlessButton`, add `activeOpacity={0.3}`. + +- Standard Buttons (Deprecated) - `BaseButton`, `RectButton` and `BorderlessButton` are still available but are now deprecated. They have been internally rewritten using the new Hooks API to resolve long-standing issues. + +- Legacy Buttons (Deprecated): The original, pre-rewrite versions are still accessible, but have been renamed with a `Legacy` prefix (e.g., `LegacyRectButton`). Although the legacy JS implementation of the buttons is still available, they also use the new host component internally. Because of that, `PureNativeButton` is no longer available in Gesture Handler 3. diff --git a/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx b/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx index 7b6245effe..f196952381 100644 --- a/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx +++ b/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx @@ -14,6 +14,9 @@ import type { NativeHandlerData } from '../hooks/gestures/native/NativeTypes'; type CallbackEventType = GestureEvent; +/** + * @deprecated `RawButton` is deprecated, use `Clickable` instead + */ export const RawButton = createNativeWrapper< React.ComponentRef, RawButtonProps diff --git a/packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts b/packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts index b48feb1b5b..b1a76b0493 100644 --- a/packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts +++ b/packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts @@ -4,6 +4,9 @@ import GestureHandlerButton, { ButtonProps, } from '../../components/GestureHandlerButton'; +/** + * @deprecated `RawButtonProps` is deprecated, use `ClickableProps` instead + */ export interface RawButtonProps extends Omit< ButtonProps, @@ -19,6 +22,9 @@ export interface RawButtonProps 'hitSlop' | 'enabled' > {} +/** + * @deprecated `BaseButtonProps` is deprecated, use `ClickableProps` instead + */ export interface BaseButtonProps extends RawButtonProps { /** * Called when the button gets pressed (analogous to `onPress` in @@ -47,6 +53,9 @@ export interface BaseButtonProps extends RawButtonProps { delayLongPress?: number | undefined; } +/** + * @deprecated `RectButtonProps` is deprecated, use `ClickableProps` instead + */ export interface RectButtonProps extends BaseButtonProps { /** * Background color that will be dimmed when button is in active state. @@ -61,6 +70,9 @@ export interface RectButtonProps extends BaseButtonProps { activeOpacity?: number | undefined; } +/** + * @deprecated `BorderlessButtonProps` is deprecated, use `ClickableProps` instead + */ export interface BorderlessButtonProps extends BaseButtonProps { /** * iOS only. diff --git a/skills/gesture-handler-3-migration/SKILL.md b/skills/gesture-handler-3-migration/SKILL.md index 2ffc89b111..64306480d6 100644 --- a/skills/gesture-handler-3-migration/SKILL.md +++ b/skills/gesture-handler-3-migration/SKILL.md @@ -170,6 +170,10 @@ The implementation of buttons has been updated, resolving most button-related is `PureNativeButton` has been removed. If encountered, inform the user that it has been removed and let them decide how to handle that case. They can achieve similar functionality with other buttons. +When migrating buttons, you should use new `Touchable` component instead. To replace `BaseButton` use `Touchable` with default props, to replace `RectButton` use `Touchable` with `activeUnderlayOpacity={0.105}` and to replace `BorderlessButton` use `Touchable` with `activeOpacity={0.3}`. + +Legacy Touchables (`TouchableOpacity`, `TouchableHighlight`, `TouchableWithoutFeedback`, `TouchableNativeFeedback`) from Gesture Handler are also deprecated and should be replaced with `Touchable`. To replace `TouchableOpacity` use `Touchable` with `activeOpacity={0.2}` and to replace `TouchableHighlight` use `Touchable` with `activeUnderlayOpacity={1}`. To replace `TouchableWithoutFeedback` use a plain `Touchable`. `TouchableNativeFeedback` can be replaced with `Touchable` by setting `androidRipple` property. At minimum, it should be set to `{foregroud: true}`, to mimic `TouchableNativeFeedback` ripple effect. + Other components have also been internally rewritten using the new hook API but are exported under their original names, so no changes are necessary on your part. However, if you need to use the previous implementation for any reason, the legacy components are also available and are prefixed with `Legacy`, e.g., `ScrollView` is now available as `LegacyScrollView`. Rename all instances of createNativeWrapper to legacy_createNativeWrapper. This includes both the import statements and the function calls.