Skip to content

Commit 4e492ec

Browse files
m-bertCopilotj-piasecki
authored
[Docs] Touchable (#4022)
## Description This PR adds docs for `Clickable` component ## Test plan Read docs 🤓 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jakub Piasecki <jakub.piasecki@swmansion.com>
1 parent e386b0c commit 4e492ec

8 files changed

Lines changed: 375 additions & 8 deletions

File tree

packages/docs-gesture-handler/docs/components/buttons.mdx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import GifGallery from '@site/components/GifGallery';
99

1010
import HeaderWithBadges from '@site/src/components/HeaderWithBadges';
1111

12+
:::danger
13+
Button components described in this section are deprecated and will be removed in the future. Please use [`Touchable`](/docs/components/touchable) instead.
14+
:::
15+
1216
<GifGallery>
1317
<img src={useBaseUrl('gifs/samplebutton.gif')} width="280" />
1418
</GifGallery>
@@ -37,7 +41,7 @@ The most basic button component does not provide any feedback and lacks props su
3741
exclusive?: boolean;
3842
```
3943

40-
Defines if more than one button could be pressed simultaneously. By default set to `true`.
44+
Defines whether pressing this button prevents other buttons exported by Gesture Handler from being pressed. By default set to `true`.
4145

4246
<HeaderWithBadges platforms={['android']}>
4347
### rippleColor
@@ -194,7 +198,7 @@ If you wish to get specific information about platforms design patterns, visit [
194198

195199
This library allows the use of native components with native feedback in adequate situations.
196200

197-
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.
201+
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.
198202

199203
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.
200204

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
---
2+
id: touchable
3+
title: Touchable
4+
sidebar_label: Touchable
5+
---
6+
7+
import HeaderWithBadges from '@site/src/components/HeaderWithBadges';
8+
9+
:::note
10+
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.
11+
:::
12+
13+
`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.
14+
15+
`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).
16+
17+
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).
18+
19+
## Replacing old buttons
20+
21+
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.
22+
23+
### RectButton
24+
25+
To replace `RectButton` with `Touchable`, simply add `activeUnderlayOpacity={0.105}` to your `Touchable`. This will animate the underlay when the button is pressed.
26+
27+
```tsx
28+
<Touchable
29+
...
30+
activeUnderlayOpacity={0.105}/>
31+
```
32+
33+
### BorderlessButton
34+
35+
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.
36+
37+
```tsx
38+
<Touchable
39+
...
40+
activeOpacity={0.3}/>
41+
```
42+
43+
## Migrating from legacy Touchable variants
44+
45+
If you were using the specialized touchable components (`TouchableOpacity`, `TouchableHighlight`, `TouchableWithoutFeedback`, or `TouchableNativeFeedback`), you can replicate their behavior with the unified `Touchable` component.
46+
47+
### TouchableOpacity
48+
49+
To replace `TouchableOpacity`, add `activeOpacity={0.2}`.
50+
51+
```tsx
52+
<Touchable
53+
...
54+
activeOpacity={0.2}/>
55+
```
56+
57+
### TouchableHighlight
58+
59+
To replace `TouchableHighlight`, add `activeUnderlayOpacity={1}`.
60+
61+
```tsx
62+
<Touchable
63+
...
64+
activeUnderlayOpacity={1}/>
65+
```
66+
67+
### TouchableWithoutFeedback
68+
69+
To replace `TouchableWithoutFeedback`, use a plain `Touchable`.
70+
71+
```tsx
72+
<Touchable ... />
73+
```
74+
75+
### TouchableNativeFeedback
76+
77+
To replicate `TouchableNativeFeedback` behavior, use the [`androidRipple`](#androidripple) prop. Make sure to set `foreground={true}`.
78+
79+
```tsx
80+
<Touchable
81+
...
82+
androidRipple={{
83+
foreground: true,
84+
}}/>
85+
```
86+
87+
## Example
88+
89+
In this example we will demonstrate how to recreate `RectButton` and `BorderlessButton` effects using the `Touchable` component.
90+
91+
<CollapsibleCode
92+
label="Show full example"
93+
expandedLabel="Hide full example"
94+
lineBounds={[7, 40]}
95+
src={`
96+
import React from 'react';
97+
import { StyleSheet, Text } from 'react-native';
98+
import {
99+
GestureHandlerRootView,
100+
Touchable,
101+
} from 'react-native-gesture-handler';
102+
103+
export default function TouchableExample() {
104+
return (
105+
<GestureHandlerRootView style={styles.container}>
106+
<Touchable
107+
onPress={() => {
108+
console.log('BaseButton built with Touchable');
109+
}}
110+
style={[styles.button, { backgroundColor: '#7d63d9' }]}>
111+
<Text style={styles.buttonText}>BaseButton</Text>
112+
</Touchable>
113+
114+
<Touchable
115+
onPress={() => {
116+
console.log('RectButton built with Touchable');
117+
}}
118+
style={[styles.button, { backgroundColor: '#4f9a84' }]}
119+
activeUnderlayOpacity={0.105}>
120+
<Text style={styles.buttonText}>RectButton</Text>
121+
</Touchable>
122+
123+
<Touchable
124+
onPress={() => {
125+
console.log('BorderlessButton built with Touchable');
126+
}}
127+
style={[styles.button, { backgroundColor: '#5f97c8' }]}
128+
activeOpacity={0.3}>
129+
<Text style={styles.buttonText}>BorderlessButton</Text>
130+
</Touchable>
131+
</GestureHandlerRootView>
132+
);
133+
}
134+
135+
const styles = StyleSheet.create({
136+
container: {
137+
flex: 1,
138+
alignItems: 'center',
139+
justifyContent: 'center',
140+
141+
gap: 20,
142+
},
143+
button: {
144+
width: 200,
145+
height: 70,
146+
borderRadius: 15,
147+
alignItems: 'center',
148+
justifyContent: 'center',
149+
},
150+
buttonText: {
151+
color: 'white',
152+
fontSize: 14,
153+
fontWeight: '600',
154+
},
155+
});
156+
`}/>
157+
158+
159+
## Properties
160+
161+
### activeOpacity
162+
163+
```ts
164+
activeOpacity?: number;
165+
```
166+
167+
Defines the opacity of the whole component when the button is active.
168+
169+
### defaultOpacity
170+
171+
```ts
172+
defaultOpacity?: number;
173+
```
174+
175+
Defines the opacity of the whole component when the button is active. By default set to `1`.
176+
177+
### activeUnderlayOpacity
178+
179+
```ts
180+
activeUnderlayOpacity?: number;
181+
```
182+
183+
Defines the opacity of the underlay when the button is active. By default set to `0`.
184+
185+
### defaultUnderlayOpacity
186+
187+
```ts
188+
defaultUnderlayOpacity?: number;
189+
```
190+
191+
Defines the initial opacity of underlay when the button is inactive. By default set to `0`.
192+
193+
### underlayColor
194+
195+
```ts
196+
underlayColor?: string;
197+
```
198+
199+
Background color of the underlay. This only takes effect when `activeUnderlayOpacity` or `defaultUnderlayOpacity` is set.
200+
201+
### exclusive
202+
203+
```ts
204+
exclusive?: boolean;
205+
```
206+
207+
Defines whether pressing this button prevents other buttons exported by Gesture Handler from being pressed. By default set to `true`.
208+
209+
<HeaderWithBadges platforms={['android']}>
210+
### touchSoundDisabled
211+
</HeaderWithBadges>
212+
213+
```ts
214+
touchSoundDisabled?: boolean;
215+
```
216+
217+
If set to `true`, the system will not play a sound when the button is pressed.
218+
219+
### onPressIn
220+
221+
<CollapsibleCode
222+
label="Show composed types definitions"
223+
expandedLabel="Hide composed types definitions"
224+
lineBounds={[0, 1]}
225+
src={`
226+
onPressIn?: (e: GestureEvent<NativeHandlerData>) => void;
227+
228+
type GestureEvent<NativeHandlerData> = {
229+
handlerTag: number;
230+
numberOfPointers: number;
231+
pointerType: PointerType;
232+
pointerInside: boolean;
233+
}
234+
235+
enum PointerType {
236+
TOUCH,
237+
STYLUS,
238+
MOUSE,
239+
KEY,
240+
OTHER,
241+
}
242+
`}/>
243+
244+
Triggered when the button gets pressed (analogous to `onPressIn` in `Pressable` from RN core).
245+
246+
### onPressOut
247+
248+
<CollapsibleCode
249+
label="Show composed types definitions"
250+
expandedLabel="Hide composed types definitions"
251+
lineBounds={[0, 1]}
252+
src={`
253+
onPressOut?: (e: GestureEvent<NativeHandlerData>) => void;
254+
255+
type GestureEvent<NativeHandlerData> = {
256+
handlerTag: number;
257+
numberOfPointers: number;
258+
pointerType: PointerType;
259+
pointerInside: boolean;
260+
}
261+
262+
enum PointerType {
263+
TOUCH,
264+
STYLUS,
265+
MOUSE,
266+
KEY,
267+
OTHER,
268+
}
269+
`}/>
270+
271+
Triggered when the button gets released or the pointer moves outside of the button area (analogous to `onPressOut` in `Pressable` from RN core).
272+
273+
### onPress
274+
275+
```ts
276+
onPress?: (pointerInside: boolean) => void;
277+
```
278+
279+
Triggered when the button gets pressed (analogous to `onPress` in `Pressable` from RN core).
280+
281+
### onLongPress
282+
283+
```ts
284+
onLongPress?: () => void;
285+
```
286+
287+
Triggered when the button gets pressed for at least [`delayLongPress`](#delaylongpress) milliseconds.
288+
289+
290+
### onActiveStateChange
291+
292+
```ts
293+
onActiveStateChange?: (active: boolean) => void;
294+
```
295+
296+
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.
297+
298+
### delayLongPress
299+
300+
```ts
301+
delayLongPress?: number;
302+
```
303+
304+
Defines the delay, in milliseconds, after which the [`onLongPress`](#onlongpress) callback gets called. By default set to `600`.
305+
306+
307+
<HeaderWithBadges platforms={['android']}>
308+
### androidRipple
309+
</HeaderWithBadges>
310+
311+
<CollapsibleCode
312+
label="Show composed types definitions"
313+
expandedLabel="Hide composed types definitions"
314+
lineBounds={[0, 1]}
315+
src={`
316+
androidRipple?: PressableAndroidRippleConfig;
317+
318+
type PressableAndroidRippleConfig = {
319+
color?: (string | OpaqueColorValue);
320+
borderless?: boolean;
321+
radius?: number;
322+
foreground?: boolean;
323+
}
324+
`}/>
325+
326+
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.

packages/docs-gesture-handler/docs/components/touchables.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
2-
id: touchables
3-
title: Touchables
4-
sidebar_label: Touchables
2+
id: legacy-touchables
3+
title: Legacy Touchables
4+
sidebar_label: Legacy Touchables
55
---
66

77
:::warning
8-
Touchables will be removed in the future version of Gesture Handler. Use [`Pressable`](/docs/components/pressable) instead.
8+
Touchables will be removed in the future version of Gesture Handler. Use [`Touchable`](/docs/components/touchable) instead.
99
:::
1010

1111
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.

packages/docs-gesture-handler/docs/guides/troubleshooting.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ This library is maintained by a very small team. Please keep this in mind when r
3232

3333
- 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.
3434
- `Native` gesture may not conform to the standard state flow due to platform specific workarounds to incorporate native views into RNGH.
35-
- 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).
35+
- 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).
3636
- 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).
3737

3838
## Multiple instances of Gesture Handler were detected

0 commit comments

Comments
 (0)