Skip to content

Commit 0e74233

Browse files
authored
[docs] Buttons (#3968)
## Description This PR updates Buttons entry in docs. ## Test plan Read docs 🤓
1 parent bdf353d commit 0e74233

File tree

3 files changed

+128
-95
lines changed

3 files changed

+128
-95
lines changed

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

Lines changed: 124 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -11,111 +11,179 @@ import GifGallery from '@site/components/GifGallery';
1111
<img src={useBaseUrl('gifs/samplebutton.gif')} width="280" />
1212
</GifGallery>
1313

14-
Gesture handler library provides native components that can act as buttons. These can be treated as a replacement to `TouchableHighlight` or `TouchableOpacity` from RN core. Gesture handler's buttons recognize touches in native which makes the recognition process deterministic, allows for rendering ripples on Android in highly performant way (`TouchableNativeFeedback` requires that touch event does a roundtrip to JS before we can update ripple effect, which makes ripples lag a bit on older phones), and provides native and platform default interaction for buttons that are placed in a scrollable container (in which case the interaction is slightly delayed to prevent button from highlighting when you fling).
14+
The Gesture Handler library offers native components that function as buttons, serving as alternatives to `TouchableHighlight` or `TouchableOpacity` from the core React Native framework. These buttons process touch recognition natively, which ensures a deterministic response. This capability significantly enhances performance; for example, it allows for immediate ripple effects on Android, unlike `TouchableNativeFeedback`, which requires a touch event roundtrip to JavaScript that can cause delays, especially noticeable on older devices. Additionally, these components handle default platform interactions natively, particularly in scrollable containers where interactions are smartly delayed to prevent unintended highlighting during a fling.
1515

16-
Currently Gesture handler library exposes three components that render native touchable elements under the hood:
16+
Gesture Handler library exposes components that render native touchable elements under the hood:
1717

18-
- [`BaseButton`](/docs/components/buttons/#basebutton)
19-
- [`RectButton`](/docs/components/buttons/#rectbutton)
20-
- [`BorderlessButton`](/docs/components/buttons/#borderlessbutton)
18+
- [`RawButton`](#rawbutton)
19+
- [`BaseButton`](#basebutton)
20+
- [`RectButton`](#rectbutton)
21+
- [`BorderlessButton`](#borderlessbutton)
2122

22-
On top of that all the buttons are wrapped with `NativeViewGestureHandler` and therefore allow for all the [common gesture handler properties](/docs/gestures/use-native-gesture#properties-common-to-all-gestures) and `NativeViewGestureHandler`'s [extra properties](/docs/gestures/use-native-gesture#properties-specific-to-nativegesture) to be applied to them.
23+
On top of that all the buttons are wrapped with [`Native gesture`](/docs/gestures/use-native-gesture) and therefore allow for all its [properties](/docs/gestures/use-native-gesture#config) to be applied to them.
2324

24-
**IMPORTANT**: In order to make buttons accessible, you have to wrap your children in a `View` with `accessible` and `accessibilityRole="button"` props.
25-
Example:
2625

27-
```javascript
28-
// Not accessible:
29-
const NotAccessibleButton = () => (
30-
<RectButton onPress={this._onPress}>
31-
<Text>Foo</Text>
32-
</RectButton>
33-
);
34-
// Accessible:
35-
const AccessibleButton = () => (
36-
<RectButton onPress={this._onPress}>
37-
<View accessible accessibilityRole="button">
38-
<Text>Bar</Text>
39-
</View>
40-
</RectButton>
41-
);
26+
## RawButton
27+
28+
The most basic button component does not provide any feedback and lacks props such as `onPress`. It serves as a foundation for other button components and is ideal if you wish to implement your own custom interactions for when the button is pressed.
29+
30+
`RawButton` accepts all [`Native gesture` props](/docs/gestures/use-native-gesture#config), [accessibility props](https://reactnative.dev/docs/accessibility#accessibility-properties), along with the following additional properties:
31+
32+
### exclusive
33+
34+
```ts
35+
exclusive?: boolean;
36+
```
37+
38+
Defines if more than one button could be pressed simultaneously. By default set to `true`.
39+
40+
### rippleColor (**Android only**)
41+
42+
```ts
43+
rippleColor?: number | ColorValue | null;
44+
```
45+
46+
Defines [color](https://reactnative.dev/docs/colors) of native [ripple](https://developer.android.com/reference/android/graphics/drawable/RippleDrawable) animation.
47+
48+
### rippleRadius (**Android only**)
49+
50+
```ts
51+
rippleRadius?: number | null;
52+
```
53+
54+
Defines radius of native [ripple](https://developer.android.com/reference/android/graphics/drawable/RippleDrawable) animation.
55+
56+
### borderless (**Android only**)
57+
58+
```ts
59+
borderless?: boolean;
60+
```
61+
62+
If set to `false`, [ripple](https://developer.android.com/reference/android/graphics/drawable/RippleDrawable) animation will render only within view bounds.
63+
64+
### foreground (**Android only**)
65+
66+
```ts
67+
foreground?: boolean;
4268
```
4369

44-
It is applicable for both iOS and Android platform. On iOS, you won't be able to even select the button, on Android you won't be able to click it in accessibility mode.
70+
Defines whether the [ripple](https://developer.android.com/reference/android/graphics/drawable/RippleDrawable) animation should be drawn on the foreground of the view.
4571

46-
## `BaseButton`
72+
### touchSoundDisabled (**Android only**)
4773

48-
Can be used as a base class if you'd like to implement some custom interaction for when the button is pressed.
74+
```ts
75+
touchSoundDisabled?: boolean;
76+
```
4977

50-
Below is a list of properties specific to `BaseButton` component:
78+
If set to `true`, the system will not play a sound when the button is pressed.
5179

52-
### `onActiveStateChange`
80+
## BaseButton
5381

54-
function that gets triggered when button changes from inactive to active and vice versa. It passes active state as a boolean variable as a first parameter for that method.
82+
Can be used as a base class if you'd like to implement some custom interaction for when the button is pressed. It has all the props of [`RawButton`](#rawbutton) and in addition to that it also provides the following props:
5583

56-
### `onPress`
84+
### onPress
5785

58-
function that gets triggered when the button gets pressed (analogous to `onPress` in `TouchableHighlight` from RN core).
86+
```ts
87+
onPress?: (pointerInside: boolean) => void;
88+
```
5989

60-
### `onLongPress`
90+
Triggered when the button gets pressed (analogous to `onPress` in `TouchableHighlight` from RN core).
6191

62-
function that gets triggered when the button gets pressed for at least `delayLongPress` milliseconds.
6392

64-
### `rippleColor` (**Android only**)
93+
### onLongPress
6594

66-
defines color of native [ripple](https://developer.android.com/reference/android/graphics/drawable/RippleDrawable) animation used since API level 21.
95+
```ts
96+
onLongPress?: () => void;
97+
```
6798

68-
### `exclusive`
99+
Triggered when the button gets pressed for at least [`delayLongPress`](#delaylongpress) milliseconds.
69100

70-
defines if more than one button could be pressed simultaneously. By default set `true`.
71101

72-
### `delayLongPress`
102+
### onActiveStateChange
73103

74-
defines the delay, in milliseconds, after which the `onLongPress` callback gets called. By default set to 600.
104+
```ts
105+
onActiveStateChange?: (active: boolean) => void;
106+
```
75107

76-
## `RectButton`
108+
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.
77109

78-
This type of button component should be used when you deal with rectangular elements or blocks of content that can be pressed, for example table rows or buttons with text and icons. This component provides a platform specific interaction, rendering a rectangular ripple on Android or highlighting the background on iOS and on older versions of Android. In addition to the props of [`BaseButton`](/docs/components/buttons#basebutton), it accepts the following:
110+
### delayLongPress
79111

80-
Below is a list of properties specific to `RectButton` component:
112+
```ts
113+
delayLongPress?: number;
114+
```
81115

82-
### `underlayColor`
116+
Defines the delay, in milliseconds, after which the [`onLongPress`](#onlongpress) callback gets called. By default set to `600`.
83117

84-
this is the background color that will be dimmed when button is in active state.
118+
## RectButton
85119

86-
### `activeOpacity` (**iOS only**)
120+
This type of button component is ideal for use with rectangular elements or content blocks that can be pressed, such as table rows or buttons featuring text and icons. It ensures platform-specific interactions, such as rendering a rectangular ripple on Android or highlighting the background on iOS and older Android versions. In addition to the props offered by [`BaseButton`](#basebutton), it accepts the following:
87121

88-
opacity applied to the underlay when button is in active state.
122+
### underlayColor
89123

90-
## `BorderlessButton`
124+
```ts
125+
underlayColor?: string;
126+
```
127+
128+
Background color that will be dimmed when button is in active state.
91129

92-
This type of button component should be used with simple icon-only or text-only buttons. The interaction will be different depending on platform: on Android a borderless ripple will be rendered (it means that the ripple will animate into a circle that can span outside of the view bounds), whereas on iOS the button will be dimmed (similar to how `TouchableOpacity` works). In addition to the props of [`BaseButton`](/docs/components/buttons#basebutton), it accepts the following:
130+
### activeOpacity (**iOS only**)
131+
132+
```ts
133+
activeOpacity?: number;
134+
```
93135

94-
Below is a list of properties specific to `BorderlessButton` component:
136+
Opacity applied to the underlay when button is in active state.
95137

96-
### `borderless` (**Android only**)
138+
## BorderlessButton
97139

98-
set this to `false` if you want the ripple animation to render only within view bounds.
140+
This type of button component should be used with simple icon-only or text-only buttons. The interaction will be different depending on platform: on Android a borderless ripple will be rendered (it means that the ripple will animate into a circle that can span outside of the view bounds), whereas on iOS the button will be dimmed (similar to how `TouchableOpacity` works). In addition to the props of [`BaseButton`](#basebutton), it accepts the following:
99141

100-
### `activeOpacity` (**iOS only**)
142+
### activeOpacity (**iOS only**)
143+
144+
```ts
145+
activeOpacity?: number;
146+
```
147+
148+
Opacity applied to the button when it is in an active state.
149+
150+
## Accessibility
151+
152+
To guarantee that buttons are fully accessible, you must wrap your children in a `View` marked as `accessible` and include the `accessibilityRole="button"` prop. This requirement applies to both iOS and Android platforms. Without these adjustments, the button won't be selectable on iOS, and on Android, it won't be clickable in accessibility mode.
153+
154+
```tsx
155+
// Not accessible:
156+
const NotAccessibleButton = () => (
157+
<RectButton onPress={this._onPress}>
158+
<Text>Foo</Text>
159+
</RectButton>
160+
);
101161

102-
opacity applied to the button when it is in an active state.
162+
// Accessible:
163+
const AccessibleButton = () => (
164+
<RectButton onPress={this._onPress}>
165+
<View accessible accessibilityRole="button">
166+
<Text>Bar</Text>
167+
</View>
168+
</RectButton>
169+
);
170+
```
103171

104172
## Design patterns
105173

106-
Components listed here were not designed to behave and look in the same way on both platforms but rather to be used for handling similar behaviour on iOS and Android taking into consideration their's design concepts.
174+
Components listed here were not designed to behave and look in the same way on both platforms but rather to be used for handling similar behaviour on iOS and Android taking into consideration their design concepts.
107175

108176
If you wish to get specific information about platforms design patterns, visit [official Apple docs](https://developer.apple.com/design/human-interface-guidelines/components/menus-and-actions/buttons) and [Material.io guideline](https://material.io/components/buttons#text-button), which widely describe how to implement coherent design.
109177

110178
This library allows to use native components with native feedback in adequate situations.
111179

112-
If you do not wish to implement custom design approach, `RectButton` and `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` 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.
180+
If you do not wish to implement 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.
113181

114182
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.
115183

116184
### Lists and action buttons
117185

118-
If you have a list with clickable items or have an action button that need to display as a separate UI block (vs being inlined in a text) you should use `RectButton`. It changes opacity on click and additionally supports a ripple effect on Android.
186+
If you have a list with clickable items or have an action button that need to display as a separate UI block (vs being inlined in a text) you should use [`RectButton`](#rectbutton). It changes opacity on click and additionally supports a [ripple effect](#ripplecolor-android-only) on Android.
119187

120188
<GifGallery>
121189
<img src={useBaseUrl('gifs/androidsettings.gif')} width="280" />
@@ -138,41 +206,3 @@ It should be used if you wish to handle non-crucial actions and supportive behav
138206
<img src={useBaseUrl('gifs/androidmail.gif')} width="280" />
139207
<img src={useBaseUrl('gifs/iosmail.gif')} width="280" />
140208
</GifGallery>
141-
142-
### `PureNativeButton`
143-
144-
Use a `PureNativeButton` for accessing the native Component used for build more complex buttons listed above.
145-
It's normally is not recommended to use, but it might be useful if we want to wrap it using Animated or Reanimated.
146-
147-
```javascript
148-
import {
149-
createNativeWrapper,
150-
PureNativeButton,
151-
} from 'react-native-gesture-handler';
152-
import Animated from 'react-native-reanimated';
153-
const { event, Value, createAnimatedComponent } = Animated;
154-
155-
const AnimatedRawButton = createNativeWrapper(
156-
createAnimatedComponent(PureNativeButton),
157-
{
158-
shouldCancelWhenOutside: false,
159-
shouldActivateOnStart: false,
160-
}
161-
);
162-
163-
export default class App extends React.Component {
164-
constructor(props) {
165-
super(props);
166-
const state = new Value();
167-
this._onGestureEvent = event([
168-
{
169-
nativeEvent: { state },
170-
},
171-
]);
172-
}
173-
174-
render() {
175-
return <AnimatedRawButton onHandlerStateChange={this._onGestureEvent} />;
176-
}
177-
}
178-
```

packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ code2={
247247

248248
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`.
249249

250-
Although the legacy JS implementation of the buttons is still available, they also use the new host component internally.
250+
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.
251251

252252
<details>
253253
<summary>Legacy buttons</summary>
@@ -258,6 +258,7 @@ Although the legacy JS implementation of the buttons is still available, they al
258258
| `BaseButton` | `LegacyBaseButton` |
259259
| `RectButton` | `LegacyRectButton` |
260260
| `BorderlessButton` | `LegacyBorderlessButton` |
261+
| `PureNativeButton` | *Not available in Gesture Handler 3* |
261262

262263
</details>
263264

skills/gesture-handler-3-migration/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ Don't suggest replacing buttons from Gesture Handler with components from React
167167

168168
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 legacy JS implementations of button components are still accessible but have been renamed with the prefix `Legacy`, e.g., `RectButton` is now available as `LegacyRectButton`. Those still use the new native component under the hood.
169169

170+
`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.
171+
170172
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`.
171173

172174
`createNativeWrapper` has been rewritten using the new hook API and exported under the original name. The old implementation is still available as `legacy_createNativeWrapper`. It also accepts new optional parameter - `detectorType`, which allows you to specify the type of the gesture detector that will be used internally. By default it uses `GestureDetector`.

0 commit comments

Comments
 (0)