diff --git a/packages/docs-gesture-handler/docs/components/pressable.mdx b/packages/docs-gesture-handler/docs/components/pressable.mdx index d7455d3973..5d717b4fc2 100644 --- a/packages/docs-gesture-handler/docs/components/pressable.mdx +++ b/packages/docs-gesture-handler/docs/components/pressable.mdx @@ -7,133 +7,237 @@ sidebar_label: Pressable import useBaseUrl from '@docusaurus/useBaseUrl'; import GifGallery from '@site/components/GifGallery'; - - - - :::info This component is a drop-in replacement for the `Pressable` component. ::: -`Pressable` is a component that can detect various stages of tap, press, and hover interactions on any of its children. + + + -### Usage: +`Pressable` is a component that can detect various stages of tap, press, and hover interactions on any of its children. -To use `Pressable`, import it in the following way: +To use `Pressable`, ensure that your app is wrapped in `GestureHandlerRootView` and import it as follows: -```js +```ts import { Pressable } from 'react-native-gesture-handler'; ``` ## Properties -### `children` +### children + +```ts +children?: + | React.ReactNode + | ((state: PressableStateCallbackType) => React.ReactNode); +``` + +Either children or a render prop that receives a boolean reflecting whether the component is currently pressed. + +### style + +```ts +style?: + | StyleProp + | ((state: PressableStateCallbackType) => StyleProp); +``` -either children or a render function that receives a boolean reflecting whether -the component is currently pressed. +Either view styles or a function that receives a boolean reflecting whether the component is currently pressed and returns view styles. -### `style` +### onPress -either view styles or a function that receives a boolean reflecting whether -the component is currently pressed and returns view styles. +```ts +onPress?: null | ((event: PressableEvent) => void); +``` -### `onPress` +Called after [`onPressOut`](#onpressout) when a single tap gesture is detected. Details about the event object can be found in the [PressableEvent](#pressableevent) section below. -called after `onPressOut` when a single tap gesture is detected. +### onPressIn -### `onPressIn` +```ts +onPressIn?: null | ((event: PressableEvent) => void); +``` -called before `onPress` when a touch is engaged. +Called before `onPress` when a touch is engaged. Details about the event object can be found in the [PressableEvent](#pressableevent) section below. -### `onPressOut` +### onPressOut -called before `onPress` when a touch is released. +```ts +onPressOut?: null | ((event: PressableEvent) => void); +``` -### `onLongPress` +Called before `onPress` when a touch is released (before [`onPress`](#onpress)). Details about the event object can be found in the [PressableEvent](#pressableevent) section below. -called immediately after pointer has been down for at least `delayLongPress` milliseconds (`500` ms by default). +### onLongPress -After `onLongPress` has been called, `onPressOut` will be called as soon as the pointer is lifted and `onPress` will not be called at all. +```ts +onLongPress?: null | ((event: PressableEvent) => void); +``` -### `cancelable` +Called immediately after pointer has been down for at least [`delayLongPress`](#delaylongpress) milliseconds. -whether a press gesture can be interrupted by a parent gesture such as a scroll event. Defaults to `true`. +After `onLongPress` has been called, [`onPressOut`](#onpressout) will be called as soon as the pointer is lifted and [`onPress`](#onpress) will not be called at all. -### `onHoverIn` (Web only) +### cancelable -called when pointer is hovering over the element. +```ts +cancelable?: null | boolean; +``` -### `onHoverOut` (Web only) +Whether a press gesture can be interrupted by a parent gesture such as a scroll event. Defaults to `true`. -called when pointer stops hovering over the element. +### onHoverIn (Web only) -### `delayHoverIn` (Web only) +```ts +onHoverIn?: null | ((event: PressableEvent) => void); +``` -duration to wait after hover in before calling `onHoverIn`. +Called when pointer is hovering over the element. -### `delayHoverOut` (Web only) +### onHoverOut (Web only) -duration to wait after hover out before calling `onHoverOut`. +```ts +onHoverOut?: null | ((event: PressableEvent) => void); +``` -### `delayLongPress` +Called when pointer stops hovering over the element. -duration (in milliseconds) from `onPressIn` before `onLongPress` is called. +### delayHoverIn (Web only) -### `disabled` +```ts +delayHoverIn?: number | null; +``` -whether the `Pressable` behavior is disabled. +Duration to wait after hover in before calling `onHoverIn`. -### `hitSlop` (Android & iOS only) +### delayHoverOut (Web only) -additional distance outside of the view in which a press is detected and `onPressIn` is triggered. +```ts +delayHoverOut?: number | null; +``` -Accepts values of type `number` or [`Rect`](https://reactnative.dev/docs/rect) +Duration to wait after hover out before calling `onHoverOut`. -### `pressRetentionOffset` (Android & iOS only) +### delayLongPress -additional distance outside of the view (or `hitSlop` if present) in which a touch is considered a -press before `onPressOut` is triggered. +```ts +delayLongPress?: null | number; +``` -Accepts values of type `number` or [`Rect`](https://reactnative.dev/docs/rect) +Duration (in milliseconds) from `onPressIn` before `onLongPress` is called. Default value is `500` ms. -### `android_disableSound` (Android only) +### disabled -if `true`, doesn't play system sound on touch. +```ts +disabled?: null | boolean; +``` -### `android_ripple` (Android only) +Whether the `Pressable` behavior is disabled. -enables the Android ripple effect and configures its color, radius and other parameters. +### hitSlop (Android & iOS only) -Accepts values of type [`RippleConfig`](https://reactnative.dev/docs/pressable#rippleconfig) +```ts +hitSlop?: null | Insets | number; +``` -### `testOnly_pressed` +Additional distance outside of the view in which a press is detected and [`onPressIn`](#onpressin) is triggered. -used only for documentation or testing (e.g. snapshot testing). +The `Insets` type is essentially the same as [`Rect`](https://reactnative.dev/docs/rect). -### `unstable_pressDelay` +### pressRetentionOffset (Android & iOS only) -duration (in milliseconds) to wait after press down before calling `onPressIn`. +```ts +pressRetentionOffset?: null | Insets | number; +``` -### Example: +Additional distance outside of the view (or [`hitSlop`](#hitslop) if present) in which a touch is considered a +press before [`onPressOut`](#onpressout) is triggered. -See the full [pressable example](https://github.com/software-mansion/react-native-gesture-handler/blob/main/apps/common-app/src/new_api/pressable/index.tsx) from `GestureHandler` example app. +The `Insets` type is essentially the same as [`Rect`](https://reactnative.dev/docs/rect). -import GestureStateFlowExample from '@site/src/examples/GestureStateFlowExample'; +### android_disableSound (Android only) -```js +```ts +android_disableSound?: null | boolean; +``` + +If `true`, doesn't play system sound on touch. + +### android_ripple (Android only) + +```ts +android_ripple?: null | PressableAndroidRippleConfig; +``` + +Enables the Android [ripple](https://developer.android.com/reference/android/graphics/drawable/RippleDrawable) effect and configures its color, radius and other parameters. + +Accepts values of type [`RippleConfig`](https://reactnative.dev/docs/pressable#rippleconfig). + +### testOnly_pressed + +```ts +testOnly_pressed?: null | boolean; +``` + +Used only for documentation or testing (e.g. snapshot testing). + +### unstable_pressDelay + +```ts +unstable_pressDelay?: number | undefined; +``` + +Duration (in milliseconds) to wait after press down before calling [`onPressIn`](#onpressin). + +## PressableEvent + +All `Pressable` callbacks receive an event object as a parameter, which is of type `PressableEvent` and has the following structure: + +```ts +export type PressableEvent = { nativeEvent: InnerPressableEvent }; + +export type InnerPressableEvent = { + changedTouches: InnerPressableEvent[]; + identifier: number; + locationX: number; + locationY: number; + pageX: number; + pageY: number; + target: number; + timestamp: number; + touches: InnerPressableEvent[]; + force?: number; +}; +``` + +## Example + +See the full example in [Gesture Handler repository](https://github.com/software-mansion/react-native-gesture-handler/blob/main/apps/common-app/src/legacy/v2_api/pressable/index.tsx). + + (pressed ? styles.highlight : styles.pressable)} - hitSlop={20} - pressRetentionOffset={20}> - - Pressable! - - + + (pressed ? styles.highlight : styles.pressable)} + hitSlop={20} + pressRetentionOffset={20}> + + Pressable! + + + ); } @@ -159,4 +263,4 @@ const styles = StyleSheet.create({ color: 'black', }, }); -``` +`}/> \ No newline at end of file diff --git a/packages/docs-gesture-handler/docs/components/reanimated-drawer-layout.mdx b/packages/docs-gesture-handler/docs/components/reanimated-drawer-layout.mdx index 8a5e2e6584..6446f11be3 100644 --- a/packages/docs-gesture-handler/docs/components/reanimated-drawer-layout.mdx +++ b/packages/docs-gesture-handler/docs/components/reanimated-drawer-layout.mdx @@ -6,141 +6,230 @@ sidebar_label: Reanimated Drawer Layout import useBaseUrl from '@docusaurus/useBaseUrl'; -Cross-platform replacement for the React Native's [DrawerLayoutAndroid](http://reactnative.dev/docs/drawerlayoutandroid.html) component. -For detailed usage of standard parameters, please refer to the [React Native docs](http://reactnative.dev/docs/drawerlayoutandroid.html). +:::info +This component acts as a cross-platform replacement for React Native's [`DrawerLayoutAndroid`](http://reactnative.dev/docs/drawerlayoutandroid.html) component, written using [Reanimated](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started). For detailed information on standard parameters, please refer to the [React Native documentation](http://reactnative.dev/docs/drawerlayoutandroid.html). +::: -### Usage: +To use `ReanimatedDrawerLayout`, first ensure that Reanimated is installed and that your app is wrapped in `GestureHandlerRootView`. You can then import it as follows: -To use it, import it in the following way: - -```js +```ts import ReanimatedDrawerLayout from 'react-native-gesture-handler/ReanimatedDrawerLayout'; ``` -## Properties: +## Properties + +### drawerType -### `drawerType` +```ts +drawerType?: DrawerType; +``` -specifies the way the drawer will be displayed. -Accepts values of the `DrawerPosition` enum. Defaults to `FRONT`. +Specifies the way the drawer will be displayed. +Accepts values of the `DrawerType` enum. Defaults to `FRONT`. -- `FRONT` the drawer will be displayed above the content view. -- `BACK` the drawer will be displayed below the content view, revealed by sliding away the content view. -- `SLIDE` the drawer will appear attached to the content view, opening it slides both the drawer and the content view. +| `drawerType` | Description | +| -- | -- | +|`FRONT` | The drawer will be displayed above the content view. | +|`BACK` | The drawer will be displayed below the content view, revealed by sliding away the content view. | +|`SLIDE` | The drawer will appear attached to the content view, opening it slides both the drawer and the content view. | | `FRONT` | `BACK` | `SLIDE` | | ----------------------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------- | | | | | -### `edgeWidth` +### edgeWidth + +```ts +edgeWidth?: number; +``` + +Width of the invisible, draggable area on the edge of the content view, which can be dragged to open the drawer. + +### hideStatusBar -width of the invisible, draggable area on the edge of the content view, which can be dragged to open the drawer. +```ts +hideStatusBar?: boolean; +``` -### `hideStatusBar` +When set to `true`, drawer component will use [StatusBar API](http://reactnative.dev/docs/statusbar.html) to hide the OS status bar when the drawer is dragged or idle in the `open` position. -a boolean value. When set to `true`, drawer component will use [StatusBar API](http://reactnative.dev/docs/statusbar.html) to hide the OS status bar when the drawer is dragged or idle in the `open` position. +### statusBarAnimation -### `statusBarAnimation` + -### `overlayColor` +May be used in combination with [`hideStatusBar`](#hidestatusbar) to select the animation used for hiding the status bar. +See [StatusBar API](http://reactnative.dev/docs/statusbar.html#statusbaranimation) docs. Defaults to `slide`. -color of the background overlay on top of the content window when the drawer is `open`. +### overlayColor + +```ts +overlayColor?: string; +``` + +Color of the background overlay on top of the content window when the drawer is `open`. This color's opacity animates from 0% to 100% as the drawer transitions from closed to open. Defaults to `rgba(0, 0, 0, 0.7)`. -### `renderNavigationView` +### renderNavigationView + +```ts +renderNavigationView: ( + progressAnimatedValue: SharedValue +) => ReactNode; +``` +A renderer function for the drawer component is provided with a `progress` parameter called `progressAnimatedValue`, which is a [`SharedValue`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#shared-value) indicating the progress of the drawer's opening or closing animation. This value is `0` when the drawer is fully closed and `1` when it is fully opened. The drawer component can use this value to animate its children during the opening or closing process. This function must return a [`ReactNode`](https://reactnative.dev/docs/react-node). + +### onDrawerClose + +```ts +onDrawerClose?: () => void; +``` + +A function which is called when the drawer has been closed. + +### onDrawerOpen + +```ts +onDrawerOpen?: () => void; +``` -a renderer function for the drawer component, provided with a `progress` parameter. +A function which is called when the drawer has been opened. -- `progress` - `SharedValue` that indicates the progress of drawer opening/closing animation. - - equals `0` when the `drawer` is closed and `1` when the `drawer` is opened - - can be used by the `drawer` component to animated its children while the `drawer` is opening or closing +### onDrawerSlide -### `onDrawerClose` +```ts +onDrawerSlide?: (position: number) => void; +``` -a function which is called when the drawer has been closed. +A function is called when the drawer is moving or animating, provided with a `position` parameter. This `position` value indicates the progress of the drawer's opening or closing animation. It equals `0` when the drawer is closed and `1` when the drawer is fully opened. This value can be utilized by the drawer component to animate its children as the drawer opens or closes. -### `onDrawerOpen` +### onDrawerStateChanged -a function which is called when the drawer has been opened. + void; -### `onDrawerSlide` +export enum DrawerState { + IDLE, + DRAGGING, + SETTLING, +} +`}/> -a function which is called when drawer is moving or animating, provided with a `progress` parameter. +A function is called when the status of the drawer changes, taking `newState` to represent the drawer's interaction state and `drawerWillShow`, which is `true` when the drawer starts animating towards the open position and `false` otherwise. -- `progress` - `SharedValue` that indicates the progress of drawer opening/closing animation. - - equals `0` when the `drawer` is closed and `1` when the `drawer` is opened - - can be used by the `drawer` component to animated its children while the `drawer` is opening or closing +### enableTrackpadTwoFingerGesture (iOS only) + +```ts +enableTrackpadTwoFingerGesture?: boolean; +``` -### `onDrawerStateChanged` +Enables two-finger gestures on supported devices, for example iPads with trackpads. If not enabled the gesture will require click + drag, with `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger the gesture. -a function which is called when the status of the drawer changes. It takes two arguments: -- `newState` - interaction state of the drawer. It can be one of the following: - - `DrawerState.IDLE` - - `DrawerState.DRAGGING` - - `DrawerState.SETTLING` -- `drawerWillShow` - `true` when `drawer` started animating to `open` position, `false` otherwise. +### children -### `enableTrackpadTwoFingerGesture` (iOS only) +```ts +children?: ReactNode | ((openValue?: SharedValue) => ReactNode); +``` -enables two-finger gestures on supported devices, for example iPads with trackpads. -If not enabled, the gesture will require click + drag, with `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger the gesture. +Either a component rendered in the content view or a function. If `children` is a function, it receives an `openValue` parameter - [`SharedValue`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#shared-value) that indicates the progress of the drawer's opening or closing animation. This value equals `0` when the drawer is closed and `1` when it is fully opened. The drawer component can use this value to animate its children during the opening or closing process. This function must return a [`ReactNode`](https://reactnative.dev/docs/react-node). -### `children` +### mouseButton (Web & Android only) -either a component that's rendered in the content view or a function. -If `children` is a function, it is provided with a `progress` parameter. +; -- `progress` - `SharedValue` that indicates the progress of drawer opening/closing animation. - - equals `0` when the `drawer` is closed and `1` when the `drawer` is opened - - can be used by the `drawer` component to animated its children while the `drawer` is opening or closing +enum MouseButton { + LEFT, + RIGHT, + MIDDLE, + BUTTON_4, + BUTTON_5, + ALL, +} +`}/> -### `mouseButton(value: MouseButton)` (Web & Android only) +Allows users to choose which mouse button should handler respond to. Arguments can be combined using `|` operator, e.g. `mouseButton(MouseButton.LEFT | MouseButton.RIGHT)`. Default value is set to `MouseButton.LEFT`. -allows users to choose which mouse button should handler respond to. -The enum `MouseButton` consists of the following predefined fields: -- `LEFT` -- `RIGHT` -- `MIDDLE` -- `BUTTON_4` -- `BUTTON_5` -- `ALL` +### enableContextMenu (Web only) -Arguments can be combined using `|` operator, e.g. `mouseButton(MouseButton.LEFT | MouseButton.RIGHT)`. Defaults to `MouseButton.LEFT`. +```ts +enableContextMenu: boolean; +``` -### `enableContextMenu(value: boolean)` (Web only) +Specifies whether context menu should be enabled after clicking on underlying view with right mouse button. Default value is set to `false` if [`MouseButton.RIGHT`](#mousebutton-web--android-only) is specified. -specifies whether context menu should be enabled after clicking on underlying view with right mouse button. Defaults to `false`. ## Methods -### `openDrawer(options)` +Using a reference to `ReanimatedDrawerLayout` allows you to manually trigger the opening and closing of the component. -`openDrawer` accepts an optional `options` parameter, which is an object with the following optional properties: +```ts +const drawerRef = useRef(null); +``` -- `initialVelocity` - the initial velocity of the object attached to the spring. Defaults to `0`. -- `animationSpeed` - controls speed of the animation. Defaults to `1`. +Both methods accept an optional `options` parameter, which allows you to customize the animation of the drawer movement. -### `closeDrawer(options)` +```ts +export type DrawerMovementOption = { + initialVelocity?: number; + animationSpeed?: number; +}; +``` + +### openDrawer -`closeDrawer` accepts an optional `options` parameter, which is an object with the following optional properties: +```ts +openDrawer: (options?: DrawerMovementOption) => void; +``` -- `initialVelocity` - initial velocity of the object attached to the spring. Defaults to `0`. -- `animationSpeed` - controls speed of the animation. Defaults to `1`. +Allows to manually open the drawer. -## Example: +### closeDrawer + +```ts +closeDrawer: (options?: DrawerMovementOption) => void; +``` -See the [reanimated drawer layout example](https://github.com/software-mansion/react-native-gesture-handler/blob/main/apps/common-app/src/release_tests/reanimatedDrawerLayout/index.tsx) from GestureHandler example app. +Allows to manually close the drawer. -```js +## Example + +Example of a `ReanimatedDrawerLayout` component can be found in [Gesture Handler repository](https://github.com/software-mansion/react-native-gesture-handler/blob/main/apps/common-app/src/new_api/components/drawer/index.tsx). + + + { }; export default function ReanimatedDrawerExample() { - const drawerRef = useRef < DrawerLayoutMethods > null; - const tapGesture = Gesture.Tap() - .runOnJS(true) - .onStart(() => drawerRef.current?.openDrawer()); + const drawerRef = useRef(null); + const tapGesture = useTapGesture({ + onDeactivate: () => { + drawerRef.current?.openDrawer(); + }, + runOnJS: true, + }); return ( - } - drawerPosition={DrawerPosition.LEFT} - drawerType={DrawerType.FRONT}> - - - - Open drawer - - - - + + } + drawerPosition={DrawerPosition.LEFT} + drawerType={DrawerType.FRONT}> + + + + Open drawer + + + + + ); } @@ -198,4 +292,4 @@ const styles = StyleSheet.create({ backgroundColor: 'pink', }, }); -``` +`}/> \ No newline at end of file diff --git a/packages/docs-gesture-handler/docs/components/reanimated_swipeable.md b/packages/docs-gesture-handler/docs/components/reanimated_swipeable.md deleted file mode 100644 index bbf58d07fc..0000000000 --- a/packages/docs-gesture-handler/docs/components/reanimated_swipeable.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -id: reanimated_swipeable -title: Reanimated Swipeable -sidebar_label: Reanimated Swipeable ---- - -import useBaseUrl from '@docusaurus/useBaseUrl'; -import GifGallery from '@site/components/GifGallery' - - - - - -:::info -This component is a drop-in replacement for the `Swipeable` component, rewritten using `Reanimated`. -::: - -Reanimated `Swipeable` allows for implementing swipeable rows or similar interaction. It renders its children within a panable container allows for horizontal swiping left and right. While swiping one of two "action" containers can be shown depends on whether user swipes left or right (containers can be rendered by `renderLeftActions` or `renderRightActions` props). - -### Usage: - -To use it, import it in the following way: - -```js -import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable'; -``` - -## Properties - -### `friction` - -a number that specifies how much the visual interaction will be delayed compared to the gesture distance. -e.g. value of `1` will indicate that the swipeable panel should exactly follow the gesture, `2` means it is going to be two times "slower". - -### `leftThreshold` - -distance from the left edge at which released panel will animate to the open state (or the open panel will animate into the closed state). By default it's a half of the panel's width. - -### `rightThreshold` - -distance from the right edge at which released panel will animate to the open state (or the open panel will animate into the closed state). By default it's a half of the panel's width. - -### `dragOffsetFromLeftEdge` - -distance that the panel must be dragged from the left edge to be considered a swipe. The default value is `10`. - -### `dragOffsetFromRightEdge` - -distance that the panel must be dragged from the right edge to be considered a swipe. The default value is `10`. - -### `overshootLeft` - -a boolean value indicating if the swipeable panel can be pulled further than the left actions panel's width. It is set to `true` by default as long as the left panel render function is present. - -### `overshootRight` - -a boolean value indicating if the swipeable panel can be pulled further than the right actions panel's width. It is set to `true` by default as long as the right panel render function is present. - -### `overshootFriction` - -a number that specifies how much the visual interaction will be delayed compared to the gesture distance at overshoot. Default value is `1`, it mean no friction, for a native feel, try `8` or above. - -### `onSwipeableOpen` - -a function that is called when `swipeable` is opened (either right or left). -Receives swipe direction as an argument. - -### `onSwipeableClose` - -a function that is called when `swipeable` is closed. -Receives swipe direction as an argument. - -### `onSwipeableWillOpen` - -a function that is called when `swipeable` starts animating on open (either right or left). -Receives swipe direction as an argument. - -### `onSwipeableWillClose` - -a function that is called when `swipeable` starts animating on close. -Receives swipe direction as an argument. - -### `onSwipeableOpenStartDrag` - -a function that is called when a user starts to drag the `swipable` to open. -Receives swipe direction as an argument. - -### `onSwipeableCloseStartDrag` - -a function that is called when a user starts to drag the `swipable` to close. -Receives swipe direction as an argument. - -### `renderLeftActions` - -a function that returns a component which will be rendered under the swipeable after swiping it to the right. -The function receives the following arguments: - -- `progress` - a `SharedValue` representing swiping progress relative to the width of the returned element. - - Equals `0` when `swipeable` is closed, `1` when `swipeable` is opened. - - When the element overshoots it's opened position the value tends towards `Infinity`. -- `translation` - a horizontal offset of the `swipeable` relative to its closed position. -- `swipeableMethods` - provides an object exposing the methods listed [here](#methods). - -This function must return a `ReactNode`. - -To support `rtl` flexbox layouts use `flexDirection` styling. - -### `renderRightActions` - -a function that returns a component which will be rendered under the swipeable after swiping it to the left. -The function receives the following arguments: - -- `progress` - a `SharedValue` representing swiping progress relative to the width of the returned element. - - Equals `0` when `swipeable` is closed, `1` when `swipeable` is opened. - - When the element overshoots it's opened position the value tends towards `Infinity`. -- `translation` - a horizontal offset of the `swipeable` relative to its closed position. -- `swipeableMethods` - provides an object exposing the methods listed [here](#methods). - -This function must return a `ReactNode`. - -To support `rtl` flexbox layouts use `flexDirection` styling. - -### `containerStyle` - -style object for the container (`Animated.View`), for example to override `overflow: 'hidden'`. - -### `childrenContainerStyle` - -style object for the children container (`Animated.View`), for example to apply `flex: 1`. - -### `simultaneousWithExternalGesture` - -A gesture configuration to be recognized simultaneously with the swipeable gesture. This is useful for allowing other gestures to work simultaneously with swipeable gesture handler. - -For example, to enable a pan gesture alongside the swipeable gesture: - -```jsx -const panGesture = Gesture.Pan(); - - - - -``` - -More details can be found in the [gesture composition documentation](../fundamentals/gesture-composition.md#simultaneouswithexternalgesture). - -### `enableTrackpadTwoFingerGesture` (iOS only) - -Enables two-finger gestures on supported devices, for example iPads with trackpads. -If not enabled the gesture will require click + drag, with `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger the gesture. - -### `mouseButton(value: MouseButton)` (Web & Android only) - -Allows users to choose which mouse button should handler respond to. The enum `MouseButton` consists of the following predefined fields: - -- `LEFT` -- `RIGHT` -- `MIDDLE` -- `BUTTON_4` -- `BUTTON_5` -- `ALL` - -Arguments can be combined using `|` operator, e.g. `mouseButton(MouseButton.LEFT | MouseButton.RIGHT)`. Default value is set to `MouseButton.LEFT`. - -### `enableContextMenu(value: boolean)` (Web only) - -Specifies whether context menu should be enabled after clicking on underlying view with right mouse button. Default value is set to `false`. - -## Methods - -Using reference to `Swipeable` it's possible to trigger some actions on it - -### `close` - -a method that closes component. - -### `openLeft` - -a method that opens component on left side. - -### `openRight` - -a method that opens component on right side. - -### `reset` - -a method that resets the swiping states of this `Swipeable` component. - -Unlike method `close`, this method does not trigger any animation. - -### Example: - -For a more in-depth presentation of differences between the new and the legacy implementations, -see [swipeable example](https://github.com/software-mansion/react-native-gesture-handler/blob/main/apps/common-app/src/release_tests/swipeableReanimation/index.tsx) from GestureHandler Example App. - -```jsx -import React from 'react'; -import { Text, StyleSheet } from 'react-native'; - -import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable'; -import Reanimated, { - SharedValue, - useAnimatedStyle, -} from 'react-native-reanimated'; - -function RightAction(prog: SharedValue, drag: SharedValue) { - const styleAnimation = useAnimatedStyle(() => { - console.log('showRightProgress:', prog.value); - console.log('appliedTranslation:', drag.value); - - return { - transform: [{ translateX: drag.value + 50 }], - }; - }); - - return ( - - Text - - ); -} - -export default function Example() { - return ( - - - Swipe me! - - - ); -} - -const styles = StyleSheet.create({ - rightAction: { width: 50, height: 50, backgroundColor: 'purple' }, - separator: { - width: '100%', - borderTopWidth: 1, - }, - swipeable: { - height: 50, - backgroundColor: 'papayawhip', - alignItems: 'center', - }, -}); -``` diff --git a/packages/docs-gesture-handler/docs/components/reanimated_swipeable.mdx b/packages/docs-gesture-handler/docs/components/reanimated_swipeable.mdx new file mode 100644 index 0000000000..71e44e83f5 --- /dev/null +++ b/packages/docs-gesture-handler/docs/components/reanimated_swipeable.mdx @@ -0,0 +1,384 @@ +--- +id: reanimated_swipeable +title: Reanimated Swipeable +sidebar_label: Reanimated Swipeable +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import GifGallery from '@site/components/GifGallery' + +:::info +This component is a drop-in replacement for the `Swipeable` component, rewritten using [Reanimated](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started). +::: + + + + + + +Reanimated `Swipeable` is designed for implementing swipeable rows or similar interactions. It places its children inside a pannable container that enables horizontal swiping to the left and right. Depending on the direction of the swipe, one of two "action" containers will be displayed, which can be configured using the [`renderLeftActions`](#renderleftactions) or [`renderRightActions`](#renderrightactions) props. + +To use Reanimated `Swipeable`, first ensure that Reanimated is installed and that your app is wrapped in `GestureHandlerRootView`. You can then import it as follows: + +```ts +import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable'; +``` + +## Properties + +### friction + +```ts +friction?: number; +``` + +Number that specifies how much the visual interaction will be delayed compared to the gesture distance. + +e.g. value of `1` will indicate that the swipeable panel should exactly follow the gesture, `2` means it is going to be two times "slower". + +### leftThreshold + +```ts +leftThreshold?: number; +``` + +Distance from the left edge at which released panel will animate to the open state (or the open panel will animate into the closed state). By default it's a half of the panel's width. + +### rightThreshold + +```ts +rightThreshold?: number; +``` + +Distance from the right edge at which released panel will animate to the open state (or the open panel will animate into the closed state). By default it's a half of the panel's width. + +### dragOffsetFromLeftEdge + +```ts +dragOffsetFromLeftEdge?: number; +``` + +Distance that the panel must be dragged from the left edge to be considered a swipe. The default value is `10`. + +### dragOffsetFromRightEdge + +```ts +dragOffsetFromRightEdge?: number; +``` + +Distance that the panel must be dragged from the right edge to be considered a swipe. The default value is `10`. + +### overshootLeft + +```ts +overshootLeft?: boolean; +``` + +A boolean value indicating if the swipeable panel can be pulled further than the left actions panel's width. It is set to `true` by default as long as the left panel render function is present. + +### overshootRight + +```ts +overshootRight?: boolean; +``` + +A boolean value indicating if the swipeable panel can be pulled further than the right actions panel's width. It is set to `true` by default as long as the right panel render function is present. + +### overshootFriction + +```ts +overshootFriction?: number; +``` + +A number specifying the delay of visual interaction compared to the gesture distance when overshooting. The default value is `1`, which means no friction. For a more native feel, try using a value of `8` or higher. + +### onSwipeableOpen + +```ts +onSwipeableOpen?: ( + direction: SwipeDirection.LEFT | SwipeDirection.RIGHT +) => void; +``` + +A function that is called when `Swipeable` is opened (either right or left). +Receives swipe direction as an argument. + +### onSwipeableClose + +```ts +onSwipeableClose?: ( + direction: SwipeDirection.LEFT | SwipeDirection.RIGHT +) => void; +``` + +A function that is called when `Swipeable` is closed. +Receives swipe direction as an argument. + +### onSwipeableWillOpen + +```ts +onSwipeableWillOpen?: ( + direction: SwipeDirection.LEFT | SwipeDirection.RIGHT +) => void; +``` + +A function that is called when `Swipeable` starts animating on open (either right or left). +Receives swipe direction as an argument. + +### onSwipeableWillClose + +```ts +onSwipeableWillClose?: ( + direction: SwipeDirection.LEFT | SwipeDirection.RIGHT +) => void; +``` + +A function that is called when `Swipeable` starts animating on close. +Receives swipe direction as an argument. + +### onSwipeableOpenStartDrag + +```ts +onSwipeableOpenStartDrag?: ( + direction: SwipeDirection.LEFT | SwipeDirection.RIGHT +) => void; +``` + +A function that is called when a user starts to drag the `Swipeable` to open. +Receives swipe direction as an argument. + +### onSwipeableCloseStartDrag + +```ts +onSwipeableCloseStartDrag?: ( + direction: SwipeDirection.LEFT | SwipeDirection.RIGHT +) => void; +``` + +A function that is called when a user starts to drag the `Swipeable` to close. +Receives swipe direction as an argument. + +### renderLeftActions + +```ts +renderLeftActions?: ( + progress: SharedValue, + translation: SharedValue, + swipeableMethods: SwipeableMethods +) => React.ReactNode; +``` + +A function that returns a component which will be rendered beneath the `Swipeable` after it is swiped to the right. This function accepts the following parameters: + +- `progress` - a [`SharedValue`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#shared-value) that represents the swiping progress relative to the width of the returned element. + - It equals `0` when the `Swipeable` is fully closed and `1` when it is fully opened. + - As the element overshoots its open position, the value approaches `Infinity`. +- `translation` - a horizontal offset of the `Swipeable` relative to its closed position. +- `swipeableMethods` - provides an object exposing methods detailed in the [methods section](#methods). + +This function must return a [`ReactNode`](https://reactnative.dev/docs/react-node). To accommodate `rtl` (right-to-left) flexbox layouts, use the [`flexDirection`](https://reactnative.dev/docs/flexbox#flex-direction) style property. + +### renderRightActions + +```ts +renderRightActions?: ( + progress: SharedValue, + translation: SharedValue, + swipeableMethods: SwipeableMethods +) => React.ReactNode; +``` + +A function that returns a component which will be rendered beneath the `Swipeable` after it is swiped to the left. This function accepts the following parameters: + +- `progress` - a [`SharedValue`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#shared-value) that represents the swiping progress relative to the width of the returned element. + - It equals `0` when the `Swipeable` is fully closed and `1` when it is fully opened. + - As the element overshoots its open position, the value approaches `Infinity`. +- `translation` - a horizontal offset of the `Swipeable` relative to its closed position. +- `swipeableMethods` - provides an object exposing methods detailed in the [methods section](#methods). + +This function must return a [`ReactNode`](https://reactnative.dev/docs/react-node). To accommodate `rtl` (right-to-left) flexbox layouts, use the [`flexDirection`](https://reactnative.dev/docs/flexbox#flex-direction) style property. + + +### containerStyle + +```ts +containerStyle?: StyleProp; +``` + +Style object for the container ([`Animated.View`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary/#animated-component)). + +### childrenContainerStyle + +```ts +childrenContainerStyle?: StyleProp; +``` + +Style object for the children container ([`Animated.View`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary/#animated-component)). + +### simultaneousWithExternalGesture + +```ts +simultaneousWithExternalGesture?: AnyGesture | AnyGesture[]; +``` + +Gestures to be recognized simultaneously with the `Swipeable` (see [gesture composition section](/docs/fundamentals/gesture-composition#simultaneouswith)). + +### requireExternalGestureToFail + +```ts +requireExternalGestureToFail?: AnyGesture | AnyGesture[]; +``` + +Gestures that `Swipeable` has to wait for before activating (see [gesture composition section](/docs/fundamentals/gesture-composition#simultaneouswith)). + + +### blocksExternalGesture + +```ts +blocksExternalGesture?: AnyGesture | AnyGesture[]; +``` + +Gestures that `Swipeable` will prevent from activating (see [gesture composition section](/docs/fundamentals/gesture-composition#simultaneouswith)). + +### enableTrackpadTwoFingerGesture (iOS only) + +```ts +enableTrackpadTwoFingerGesture?: boolean; +``` + +Enables two-finger gestures on supported devices, for example iPads with trackpads. If not enabled the gesture will require click + drag, with `enableTrackpadTwoFingerGesture` swiping with two fingers will also trigger the gesture. + +### mouseButton (Web & Android only) + +; + +enum MouseButton { + LEFT, + RIGHT, + MIDDLE, + BUTTON_4, + BUTTON_5, + ALL, +} +`}/> + +Allows users to choose which mouse button should handler respond to. Arguments can be combined using `|` operator, e.g. `mouseButton(MouseButton.LEFT | MouseButton.RIGHT)`. Default value is set to `MouseButton.LEFT`. + +### enableContextMenu (Web only) + +```ts +enableContextMenu: boolean; +``` + +Specifies whether context menu should be enabled after clicking on underlying view with right mouse button. Default value is set to `false`. + + +## Methods + +Using a reference to `Swipeable` allows you to manually trigger the opening and closing of the component, as well as reset its swiping state. + +```ts +const swipeableRef = useRef(null); +``` + +### close + +```ts +close: () => void; +``` + +A method that closes component. + +### openLeft + +```ts +openLeft: () => void; +``` + +A method that opens component on left side. + +### openRight + +```ts +openRight: () => void; +``` + +A method that opens component on right side. + +### reset + +```ts +reset: () => void; +``` + +A method that resets the swiping states of this `Swipeable` component. Unlike [`close`](#close), this method does not trigger any animation. + +## Example + +Example of a `Swipeable` component can be found in [Gesture Handler repository](https://github.com/software-mansion/react-native-gesture-handler/blob/main/apps/common-app/src/new_api/components/swipeable/index.tsx). + +, drag: SharedValue) { + const styleAnimation = useAnimatedStyle(() => { + console.log('showRightProgress:', prog.value); + console.log('appliedTranslation:', drag.value); + + return { + transform: [{ translateX: drag.value + 50 }], + }; + }); + + return ( + + Text + + ); +} + +export default function Example() { + return ( + + + Swipe me! + + + ); +} + +const styles = StyleSheet.create({ + rightAction: { width: 50, height: 50, backgroundColor: 'purple' }, + separator: { + width: '100%', + borderTopWidth: 1, + }, + swipeable: { + height: 50, + backgroundColor: 'papayawhip', + alignItems: 'center', + }, +}); +`}/> \ No newline at end of file diff --git a/packages/docs-gesture-handler/docs/components/touchables.md b/packages/docs-gesture-handler/docs/components/touchables.md index 6d1f19107f..23129eecf9 100644 --- a/packages/docs-gesture-handler/docs/components/touchables.md +++ b/packages/docs-gesture-handler/docs/components/touchables.md @@ -5,7 +5,7 @@ sidebar_label: Touchables --- :::warning -Touchables will be removed in the future version of Gesture Handler. Use Pressable instead. +Touchables will be removed in the future version of Gesture Handler. Use [`Pressable`](/docs/components/pressable) instead. ::: Gesture Handler library provides an implementation of RN's touchable components that are based on [native buttons](buttons.mdx) and does not rely on 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. @@ -28,12 +28,12 @@ Our intention was to make switch for these touchables as simple as possible. In need only to change imports of touchables. :::info -Gesture Handler's TouchableOpacity uses native driver for animations by default. If this causes problems for you, you can set `useNativeAnimations` prop to false. +Gesture Handler's `TouchableOpacity` uses native driver for animations by default. If this causes problems for you, you can set `useNativeAnimations` prop to false. ::: ### Example: -```javascript +```ts import { TouchableNativeFeedback, TouchableHighlight, @@ -44,7 +44,7 @@ import { has to be replaced with: -```javascript +```ts import { TouchableNativeFeedback, TouchableHighlight, @@ -53,4 +53,4 @@ import { } from 'react-native-gesture-handler'; ``` -For a comparison of both touchable implementations see our [touchables example](https://github.com/software-mansion/react-native-gesture-handler/blob/main/apps/common-app/src/release_tests/touchables/index.tsx) +For a comparison of both touchable implementations see our [touchables example](https://github.com/software-mansion/react-native-gesture-handler/blob/main/apps/common-app/src/legacy/release_tests/touchables/index.tsx) diff --git a/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Competing.tsx b/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Competing.tsx new file mode 100644 index 0000000000..01e66327d0 --- /dev/null +++ b/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Competing.tsx @@ -0,0 +1,48 @@ +import { View, StyleSheet } from 'react-native'; +import { + GestureDetector, + usePanGesture, + useLongPressGesture, + GestureHandlerRootView, + useCompetingGestures, +} from 'react-native-gesture-handler'; + +export default function App() { + const panGesture = usePanGesture({ + onUpdate: () => { + console.log('Pan'); + }, + }); + const longPressGesture = useLongPressGesture({ + onDeactivate: (_, success) => { + if (success) { + console.log('Long Press'); + } + }, + }); + + const gesture = useCompetingGestures(panGesture, longPressGesture); + + return ( + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-around', + }, + box: { + height: 120, + width: 120, + backgroundColor: '#b58df1', + borderRadius: 20, + marginBottom: 30, + }, +}); diff --git a/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Exclusive.tsx b/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Exclusive.tsx new file mode 100644 index 0000000000..bf1bc86999 --- /dev/null +++ b/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Exclusive.tsx @@ -0,0 +1,49 @@ +import { StyleSheet, View } from 'react-native'; +import { + GestureDetector, + GestureHandlerRootView, + useTapGesture, + useExclusiveGestures, +} from 'react-native-gesture-handler'; + +export default function App() { + const singleTap = useTapGesture({ + onDeactivate: (_, success) => { + if (success) { + console.log('Single tap!'); + } + }, + }); + + const doubleTap = useTapGesture({ + numberOfTaps: 2, + onDeactivate: (_, success) => { + if (success) { + console.log('Double tap!'); + } + }, + }); + + const taps = useExclusiveGestures(doubleTap, singleTap); + + return ( + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-around', + }, + box: { + width: 100, + height: 100, + backgroundColor: 'plum', + }, +}); diff --git a/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Simultaneous.tsx b/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Simultaneous.tsx new file mode 100644 index 0000000000..a6e0452ba7 --- /dev/null +++ b/packages/docs-gesture-handler/docs/fundamentals/_examples/hooks/Simultaneous.tsx @@ -0,0 +1,95 @@ +import { StyleSheet } from 'react-native'; +import { + GestureDetector, + GestureHandlerRootView, + usePanGesture, + usePinchGesture, + useRotationGesture, + useSimultaneousGestures, +} from 'react-native-gesture-handler'; +import Animated, { + useSharedValue, + useAnimatedStyle, +} from 'react-native-reanimated'; + +export default function App() { + const offset = useSharedValue({ x: 0, y: 0 }); + const start = useSharedValue({ x: 0, y: 0 }); + + const scale = useSharedValue(1); + const savedScale = useSharedValue(1); + const rotation = useSharedValue(0); + const savedRotation = useSharedValue(0); + + const animatedStyles = useAnimatedStyle(() => { + return { + transform: [ + { translateX: offset.value.x }, + { translateY: offset.value.y }, + { scale: scale.value }, + { rotateZ: `${rotation.value}rad` }, + ], + }; + }); + + const dragGesture = usePanGesture({ + averageTouches: true, + onUpdate: (e) => { + offset.value = { + x: e.translationX + start.value.x, + y: e.translationY + start.value.y, + }; + }, + onDeactivate: () => { + start.value = { + x: offset.value.x, + y: offset.value.y, + }; + }, + }); + + const zoomGesture = usePinchGesture({ + onUpdate: (e) => { + scale.value = savedScale.value * e.scale; + }, + onDeactivate: () => { + savedScale.value = scale.value; + }, + }); + + const rotationGesture = useRotationGesture({ + onUpdate: (e) => { + rotation.value = savedRotation.value + e.rotation; + }, + onDeactivate: () => { + savedRotation.value = rotation.value; + }, + }); + + const composedGesture = useSimultaneousGestures( + dragGesture, + zoomGesture, + rotationGesture + ); + + return ( + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-around', + }, + box: { + width: 100, + height: 100, + backgroundColor: 'blue', + }, +}); diff --git a/packages/docs-gesture-handler/docs/fundamentals/_examples/props/Block.tsx b/packages/docs-gesture-handler/docs/fundamentals/_examples/props/Block.tsx new file mode 100644 index 0000000000..8b9916d22f --- /dev/null +++ b/packages/docs-gesture-handler/docs/fundamentals/_examples/props/Block.tsx @@ -0,0 +1,96 @@ +import React, { useState } from 'react'; +import { StyleSheet } from 'react-native'; +import { + GestureDetector, + GestureHandlerRootView, + ScrollView, + NativeGesture, + usePinchGesture, +} from 'react-native-gesture-handler'; +import Animated, { + useSharedValue, + useAnimatedStyle, + withTiming, +} from 'react-native-reanimated'; + +const ITEMS = ['red', 'green', 'blue', 'yellow']; + +type ItemProps = { + backgroundColor: string; + scrollGesture: NativeGesture | null; +}; + +function Item({ backgroundColor, scrollGesture }: ItemProps) { + const scale = useSharedValue(1); + const zIndex = useSharedValue(1); + + const pinch = usePinchGesture({ + onBegin: () => { + zIndex.value = 100; + }, + onUpdate: (e) => { + scale.value *= e.scaleChange; + }, + onFinalize: () => { + scale.value = withTiming(1, undefined, (finished) => { + if (finished) { + zIndex.value = 1; + } + }); + }, + block: scrollGesture ?? undefined, + }); + + const animatedStyles = useAnimatedStyle(() => ({ + transform: [{ scale: scale.value }], + zIndex: zIndex.value, + })); + + return ( + + + + ); +} + +export default function App() { + const [scrollGesture, setScrollGesture] = useState( + null + ); + + return ( + + { + if (!scrollGesture || scrollGesture.tag !== gesture.tag) { + setScrollGesture(gesture); + } + }}> + {ITEMS.map((item) => ( + + ))} + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + item: { + flex: 1, + aspectRatio: 1, + }, +}); diff --git a/packages/docs-gesture-handler/docs/fundamentals/_examples/props/RequireToFail.tsx b/packages/docs-gesture-handler/docs/fundamentals/_examples/props/RequireToFail.tsx new file mode 100644 index 0000000000..443794ada9 --- /dev/null +++ b/packages/docs-gesture-handler/docs/fundamentals/_examples/props/RequireToFail.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { + GestureDetector, + GestureHandlerRootView, + useTapGesture, +} from 'react-native-gesture-handler'; + +export default function App() { + const innerTap = useTapGesture({ + numberOfTaps: 2, + onDeactivate: (_, success) => { + if (success) { + console.log('inner tap'); + } + }, + }); + + const outerTap = useTapGesture({ + onDeactivate: (_, success) => { + if (success) { + console.log('outer tap'); + } + }, + requireToFail: innerTap, + }); + + return ( + + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + outer: { + width: 250, + height: 250, + backgroundColor: 'lightblue', + }, + inner: { + width: 100, + height: 100, + backgroundColor: 'blue', + alignSelf: 'center', + }, +}); diff --git a/packages/docs-gesture-handler/docs/fundamentals/_examples/props/SimultaneousWith.tsx b/packages/docs-gesture-handler/docs/fundamentals/_examples/props/SimultaneousWith.tsx new file mode 100644 index 0000000000..3fca4b0e05 --- /dev/null +++ b/packages/docs-gesture-handler/docs/fundamentals/_examples/props/SimultaneousWith.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { + GestureDetector, + GestureHandlerRootView, + useTapGesture, +} from 'react-native-gesture-handler'; + +export default function App() { + const innerTap = useTapGesture({ + onDeactivate: (_, success) => { + if (success) { + console.log('inner tap'); + } + }, + }); + + const outerTap = useTapGesture({ + onDeactivate: (_, success) => { + if (success) { + console.log('outer tap'); + } + }, + simultaneousWith: innerTap, + }); + + return ( + + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + outer: { + width: 250, + height: 250, + backgroundColor: 'lightblue', + }, + inner: { + width: 100, + height: 100, + backgroundColor: 'blue', + alignSelf: 'center', + }, +}); diff --git a/packages/docs-gesture-handler/docs/fundamentals/animated-interactions.mdx b/packages/docs-gesture-handler/docs/fundamentals/animated-interactions.mdx index 1ecae5e69f..bca2e3e4a6 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/animated-interactions.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/animated-interactions.mdx @@ -7,12 +7,12 @@ sidebar_position: 5 import CollapsibleCode from '@site/src/components/CollapsibleCode'; -Using hook API allows for smooth integration with the [Animated API](https://reactnative.dev/docs/animated) by allowing for passing an `Animated.event` as the argument to the `onUpdate` callback. The event mapping of `Animated.event` depends on the `useNativeDriver` property. +Using hook API allows for smooth integration with the [Animated API](https://reactnative.dev/docs/animated) by allowing for passing an `Animated.event` as the argument to the [`onUpdate`](/docs/fundamentals/callbacks-events#onupdate) callback. The event mapping of `Animated.event` depends on the [`useNativeDriver`](https://reactnative.dev/docs/animated#using-the-native-driver) property. When using Animated API, remember to set `useAnimated` property to `true`. :::danger Mixing Reanimated and Animated -It is not possible to mix `Reanimated` and `Animated` within any of the [gesture detectors](/docs/fundamentals/gesture-detectors). +It is not possible to mix Reanimated and Animated within any of the [gesture detectors](/docs/fundamentals/gesture-detectors). ::: `TouchEvent` carries information about raw touch events, like touching the screen or moving the finger. + +- `eventType` - Type of the current event - whether the finger was placed on the screen, moved, lifted or cancelled. + +- `changedTouches` - An array of objects where every object represents a single touch. Contains information only about the touches that were affected by the event i.e. those that were placed down, moved, lifted or cancelled. + +- `allTouches` - An array of objects where every object represents a single touch. Contains information about all active touches. + +- `numberOfTouches` - Number representing the count of currently active touches. + +`TouchData` contains information about a single touch. + +- `id` - A number representing id of the touch. It may be used to track the touch between events as the id will not change while it is being tracked. + +- `x` - X coordinate of the current position of the touch relative to the view attached to the [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector). Expressed in point units. + +- `y` - Y coordinate of the current position of the touch relative to the view attached to the [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector). Expressed in point units. + +- `absoluteX` - X coordinate of the current position of the touch relative to the window. The value is expressed in point units. It is recommended to use it instead of `x` in cases when the original view can be transformed as an effect of the gesture. + +- `absoluteY` - Y coordinate of the current position of the touch relative to the window. The value is expressed in point units. It is recommended to use it instead of `y` in cases when the original view can be transformed as an effect of the gesture. + +:::danger +Don't rely on the order of items in the `changedTouches`/`allTouches` arrays as it may change during the gesture, instead use the `id` attribute to track individual touches across events. +::: + diff --git a/packages/docs-gesture-handler/docs/fundamentals/gesture-composition.md b/packages/docs-gesture-handler/docs/fundamentals/gesture-composition.md deleted file mode 100644 index 6463507f1a..0000000000 --- a/packages/docs-gesture-handler/docs/fundamentals/gesture-composition.md +++ /dev/null @@ -1,471 +0,0 @@ ---- -id: gesture-composition -title: Gesture composition & interactions -sidebar_label: Gesture composition & interactions -sidebar_position: 6 ---- - -RNGH3 simplifies gesture interaction through dedicated composition hooks and configuration properties. To choose the right approach, simply ask: Are all the gestures attached to the same component? - -- If yes: Use composition hooks. These allow you to bundle multiple gestures—including previously composed ones—into a single object for a GestureDetector. - -- If no: Use relation properties to manually define how gestures interact. Since these properties also support composed gestures, you can mix both methods for more complex layouts. - -## Composition hooks - -### useCompetingGestures - -Only one of the provided gestures can become active at the same time. The first gesture to become active will cancel the rest of the gestures. It accepts variable number of arguments. - -For example, lets say that you have a component that you want to make draggable but you also want to show additional options on long press. Presumably you would not want the component to move after the long press activates. You can accomplish this using `useCompetingGestures`: - -```js -import { View, StyleSheet } from 'react-native'; -import { - GestureDetector, - usePanGesture, - useLongPressGesture, - GestureHandlerRootView, - useCompetingGestures, -} from 'react-native-gesture-handler'; - -export default function App() { - const panGesture = usePanGesture({ - onUpdate: () => { - console.log('Pan'); - }, - }); - const longPressGesture = useLongPressGesture({ - onDeactivate: (_, success) => { - if (success) { - console.log('Long Press'); - } - }, - }); - - const gesture = useCompetingGestures(panGesture, longPressGesture); - - return ( - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'space-around', - }, - box: { - height: 120, - width: 120, - backgroundColor: '#b58df1', - borderRadius: 20, - marginBottom: 30, - }, -}); -``` - -### useSimultaneousGestures - -All of the provided gestures can activate at the same time. Activation of one will not cancel the other. - -For example, if you want to make a gallery app, you might want user to be able to zoom, rotate and pan around photos. You can do it with `useSimultaneousGestures`: - -> Note: the `useSharedValue` and `useAnimatedStyle` are part of [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/). - -```js -import { StyleSheet } from 'react-native'; -import { - GestureDetector, - GestureHandlerRootView, - usePanGesture, - usePinchGesture, - useRotationGesture, - useSimultaneousGestures, -} from 'react-native-gesture-handler'; -import Animated, { - useSharedValue, - useAnimatedStyle, -} from 'react-native-reanimated'; - -export default function App() { - const offset = useSharedValue({ x: 0, y: 0 }); - const start = useSharedValue({ x: 0, y: 0 }); - - const scale = useSharedValue(1); - const savedScale = useSharedValue(1); - const rotation = useSharedValue(0); - const savedRotation = useSharedValue(0); - - const animatedStyles = useAnimatedStyle(() => { - return { - transform: [ - { translateX: offset.value.x }, - { translateY: offset.value.y }, - { scale: scale.value }, - { rotateZ: `${rotation.value}rad` }, - ], - }; - }); - - const dragGesture = usePanGesture({ - averageTouches: true, - onUpdate: (e) => { - offset.value = { - x: e.translationX + start.value.x, - y: e.translationY + start.value.y, - }; - }, - onDeactivate: () => { - start.value = { - x: offset.value.x, - y: offset.value.y, - }; - }, - }); - - const zoomGesture = usePinchGesture({ - onUpdate: (e) => { - scale.value = savedScale.value * e.scale; - }, - onDeactivate: () => { - savedScale.value = scale.value; - }, - }); - - const rotationGesture = useRotationGesture({ - onUpdate: (e) => { - rotation.value = savedRotation.value + e.rotation; - }, - onDeactivate: () => { - savedRotation.value = rotation.value; - }, - }); - - const composedGesture = useSimultaneousGestures( - dragGesture, - zoomGesture, - rotationGesture - ); - - return ( - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'space-around', - }, - box: { - width: 100, - height: 100, - backgroundColor: 'blue', - }, -}); -``` - -### useExclusiveGestures - -Only one of the provided gestures can become active. Priority is determined by the order of the aguments, where the first gesture has the highest priority, and the last has the lowest. A gesture can activate only after all higher-priority gestures before it have failed. - -For example, if you want to make a component that responds to single tap as well as to a double tap, you can accomplish that using `useExclusiveGestures`: - -```js -import { StyleSheet, View } from 'react-native'; -import { - GestureDetector, - GestureHandlerRootView, - useTapGesture, - useExclusiveGestures, -} from 'react-native-gesture-handler'; - -export default function App() { - const singleTap = useTapGesture({ - onDeactivate: (_, success) => { - if (success) { - console.log('Single tap!'); - } - }, - }); - - const doubleTap = useTapGesture({ - numberOfTaps: 2, - onDeactivate: (_, success) => { - if (success) { - console.log('Double tap!'); - } - }, - }); - - const taps = useExclusiveGestures(doubleTap, singleTap); - - return ( - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'space-around', - }, - box: { - width: 100, - height: 100, - backgroundColor: 'plum', - }, -}); -``` - -## Cross-component interactions - -### requireToFail - -`requireToFail` allows to delay activation of the handler until all handlers passed as arguments to this method fail (or don't begin at all). - -For example, you may want to have two nested components, both of them can be tapped by the user to trigger different actions: outer view requires one tap, but the inner one requires 2 taps. If you don't want the first tap on the inner view to activate the outer handler, you must make the outer gesture wait until the inner one fails: - -```jsx -import React from 'react'; -import { View, StyleSheet } from 'react-native'; -import { - GestureDetector, - GestureHandlerRootView, - useTapGesture, -} from 'react-native-gesture-handler'; - -export default function Example() { - const innerTap = useTapGesture({ - numberOfTaps: 2, - onDeactivate: (_, success) => { - if (success) { - console.log('inner tap'); - } - }, - }); - - const outerTap = useTapGesture({ - onDeactivate: (_, success) => { - if (success) { - console.log('outer tap'); - } - }, - requireToFail: innerTap, - }); - - return ( - - - - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - outer: { - width: 250, - height: 250, - backgroundColor: 'lightblue', - }, - inner: { - width: 100, - height: 100, - backgroundColor: 'blue', - alignSelf: 'center', - }, -}); -``` - -### block - -`block` works similarly to `requireToFail` but the direction of the relation is reversed - instead of being one-to-many relation, it's many-to-one. It's especially useful for making lists where the `ScrollView` component needs to wait for every gesture underneath it. All that's required to do is to pass a ref, for example: - -```jsx -import React, { useState } from 'react'; -import { StyleSheet } from 'react-native'; -import { - GestureDetector, - GestureHandlerRootView, - ScrollView, - NativeGesture, - usePinchGesture, -} from 'react-native-gesture-handler'; -import Animated, { - useSharedValue, - useAnimatedStyle, - withTiming, -} from 'react-native-reanimated'; - -const ITEMS = ['red', 'green', 'blue', 'yellow']; - -export default function Example() { - const [scrollGesture, setScrollGesture] = useState( - null - ); - - return ( - - { - if (!scrollGesture || scrollGesture.tag !== gesture.tag) { - setScrollGesture(gesture); - } - }}> - {ITEMS.map((item) => ( - - ))} - - - ); -} - -type ItemProps = { - backgroundColor: string; - scrollGesture: NativeGesture | null; -}; - -function Item({ backgroundColor, scrollGesture }: ItemProps) { - const scale = useSharedValue(1); - const zIndex = useSharedValue(1); - - const pinch = usePinchGesture({ - onBegin: () => { - zIndex.value = 100; - }, - onUpdate: (e) => { - scale.value *= e.scaleChange; - }, - onFinalize: () => { - scale.value = withTiming(1, undefined, (finished) => { - if (finished) { - zIndex.value = 1; - } - }); - }, - block: scrollGesture ?? undefined, - }); - - const animatedStyles = useAnimatedStyle(() => ({ - transform: [{ scale: scale.value }], - zIndex: zIndex.value, - })); - - return ( - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - item: { - flex: 1, - aspectRatio: 1, - }, -}); -``` - -### simultaneousWith - -`simultaneousWith` allows gestures across different components to be recognized simultaneously. For example, you may want to have two nested views, both with tap gesture attached. Both of them require one tap, but tapping the inner one should also activate the gesture attached to the outer view: - -```jsx -import React from 'react'; -import { View, StyleSheet } from 'react-native'; -import { - GestureDetector, - GestureHandlerRootView, - useTapGesture, -} from 'react-native-gesture-handler'; - -export default function Example() { - const innerTap = useTapGesture({ - onDeactivate: (_, success) => { - if (success) { - console.log('inner tap'); - } - }, - }); - - const outerTap = useTapGesture({ - onDeactivate: (_, success) => { - if (success) { - console.log('outer tap'); - } - }, - simultaneousWith: innerTap, - }); - - return ( - - - - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - outer: { - width: 250, - height: 250, - backgroundColor: 'lightblue', - }, - inner: { - width: 100, - height: 100, - backgroundColor: 'blue', - alignSelf: 'center', - }, -}); -``` diff --git a/packages/docs-gesture-handler/docs/fundamentals/gesture-composition.mdx b/packages/docs-gesture-handler/docs/fundamentals/gesture-composition.mdx new file mode 100644 index 0000000000..c23566899d --- /dev/null +++ b/packages/docs-gesture-handler/docs/fundamentals/gesture-composition.mdx @@ -0,0 +1,97 @@ +--- +id: gesture-composition +title: Gesture composition & interactions +sidebar_label: Gesture composition & interactions +sidebar_position: 6 +--- + +import CollapsibleCode from '@site/src/components/CollapsibleCode'; + +import Competing from '!!raw-loader!.//\_examples/hooks/Competing'; +import Simultaneous from '!!raw-loader!.//\_examples/hooks/Simultaneous'; +import Exclusive from '!!raw-loader!.//\_examples/hooks/Exclusive'; + +import RequireToFail from '!!raw-loader!.//\_examples/props/RequireToFail'; +import Block from '!!raw-loader!.//\_examples/props/Block'; +import SimultaneousWith from '!!raw-loader!.//\_examples/props/SimultaneousWith'; + +RNGH3 simplifies gesture interaction through dedicated composition hooks and configuration properties. To choose the right approach, simply ask: Are all the gestures attached to the same component? + +- If yes: Use composition hooks. These allow you to bundle multiple gestures—including previously composed ones—into a single object for a GestureDetector. + +- If no: Use relation properties to manually define how gestures interact. Since these properties also support composed gestures, you can mix both methods for more complex layouts. + +## Composition hooks + +### useCompetingGestures + +Only one of the provided gestures can become active at the same time. The first gesture to become active will cancel the rest of the gestures. It accepts variable number of arguments. + +For example, lets say that you have a component that you want to make draggable but you also want to show additional options on long press. Presumably you would not want the component to move after the long press activates. You can accomplish this using `useCompetingGestures`: + + + +### useSimultaneousGestures + +All of the provided gestures can activate at the same time. Activation of one will not cancel the other. + +For example, if you want to make a gallery app, you might want user to be able to zoom, rotate and pan around photos. You can do it with `useSimultaneousGestures`: + +> Note: the `useSharedValue` and `useAnimatedStyle` are part of [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/). + + + +### useExclusiveGestures + +Only one of the provided gestures can become active. Priority is determined by the order of the aguments, where the first gesture has the highest priority, and the last has the lowest. A gesture can activate only after all higher-priority gestures before it have failed. + +For example, if you want to make a component that responds to single tap as well as to a double tap, you can accomplish that using `useExclusiveGestures`: + + + + +## Cross-component interactions + +### requireToFail + +`requireToFail` allows to delay activation of the handler until all handlers passed as arguments to this method fail (or don't begin at all). + +For example, you may want to have two nested components, both of them can be tapped by the user to trigger different actions: outer view requires one tap, but the inner one requires 2 taps. If you don't want the first tap on the inner view to activate the outer handler, you must make the outer gesture wait until the inner one fails: + + + +### block + +`block` works similarly to `requireToFail` but the direction of the relation is reversed - instead of being one-to-many relation, it's many-to-one. It's especially useful for making lists where the `ScrollView` component needs to wait for every gesture underneath it. All that's required to do is to pass a ref, for example: + + + +### simultaneousWith + +`simultaneousWith` allows gestures across different components to be recognized simultaneously. For example, you may want to have two nested views, both with tap gesture attached. Both of them require one tap, but tapping the inner one should also activate the gesture attached to the outer view: + + diff --git a/packages/docs-gesture-handler/docs/fundamentals/gesture-detector.mdx b/packages/docs-gesture-handler/docs/fundamentals/gesture-detector.mdx index 4756567ec6..99a10cdd45 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/gesture-detector.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/gesture-detector.mdx @@ -10,9 +10,9 @@ import CollapsibleCode from '@site/src/components/CollapsibleCode'; ## Gesture Detector -The `GestureDetector` is a key component of `react-native-gesture-handler`. It supports gestures created either using the hooks API or the builder pattern. Additionally, it allows for the recognition of multiple gestures through [gesture composition](/docs/fundamentals/gesture-composition). `GestureDetector` interacts closely with [`Reanimated`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/). For more details, refer to the [Integration with Reanimated](/docs/fundamentals/reanimated-interactions) section. +The `GestureDetector` is a key component of `react-native-gesture-handler`. It supports gestures created either using the hook-based API or the builder pattern. Additionally, it allows for the recognition of multiple gestures through [gesture composition](/docs/fundamentals/gesture-composition). `GestureDetector` interacts closely with [`Reanimated`](https://docs.swmansion.com/react-native-reanimated/). For more details, refer to the [Integration with Reanimated](/docs/fundamentals/reanimated-interactions) section. -When using hook API, you can also integrate it directly with the [Animated API](https://reactnative.dev/docs/animated). More on that can be found in [Integreation with Animated](/docs/fundamentals/animated-interactions) section. +When using hook API, you can also integrate it directly with the [Animated API](https://reactnative.dev/docs/animated). More on that can be found in [Integration with Animated](/docs/fundamentals/animated-interactions) section. :::danger @@ -64,15 +64,15 @@ Here are some of the most common use cases for virtual gesture detectors. #### SVG -You can combine `VirtualGestureDetector` with `react-native-svg` to add gesture handling to individual SVG elements. +You can combine `VirtualGestureDetector` with [`react-native-svg`](https://github.com/software-mansion/react-native-svg) to add gesture handling to individual `SVG` elements. - // highlight-next-line - - - // highlight-next-line - - {}} - /> - // highlight-next-line - - - - // highlight-next-line + + + {}} + /> + + ); @@ -124,9 +118,6 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - box: { - backgroundColor: '#b58df1', - }, }); `}/> @@ -210,7 +201,7 @@ This parameter allows to specify which `userSelect` property should be applied t ### touchAction (Web only) ```ts -userSelect: TouchAction; +touchAction: TouchAction; ``` This parameter allows to specify which `touchAction` property should be applied to underlying view. Supports all CSS [touch-action](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/touch-action) values. Default value is set to `"none"`. diff --git a/packages/docs-gesture-handler/docs/fundamentals/installation.mdx b/packages/docs-gesture-handler/docs/fundamentals/installation.mdx index 97d884fede..c1500b2c75 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/installation.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/installation.mdx @@ -11,11 +11,19 @@ import { GestureHandlerCompatibility } from '../../components/Compatibility'; ## Requirements +### Compatibility with React Native versions + `react-native-gesture-handler` supports the three latest minor releases of `react-native`. -In order to fully utilize the [touch events](/docs/gestures/touch-events/) you also need to use `react-native-reanimated` 2.3.0 or newer. +### Running gestures on the UI thread + +Using Reanimated is the recommended method of handling gesture-driven interactions on the UI thread. In order to use it, you need to install [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) along with [`react-native-worklets`](https://docs.swmansion.com/react-native-worklets/). Another approach is to use React Native's [Animated API](https://reactnative.dev/docs/animated). + +For more details on how to implement these, refer to the dedicated sections for [Reanimated](/docs/fundamentals/reanimated-interactions) and [Animated](/docs/fundamentals/animated-interactions). + +## Setup Setting up `react-native-gesture-handler` is pretty straightforward: @@ -52,17 +60,22 @@ export default function App() { ); } ``` +Keep `GestureHandlerRootView` as close to the actual root of the app as possible. It's the entry point for all gestures and all [gesture relations](/docs/fundamentals/gesture-composition). The gestures won't be recognized outside of the root view, and relations only work between gestures mounted under the same root view. -If you don't provide anything to the `styles` prop, it will default to `flex: 1`. If you want to customize the styling of the root view, don't forget to also include `flex: 1` in the custom style, otherwise your app won't render anything. Keep `GestureHandlerRootView` as close to the actual root of the app as possible. It's the entry point for all gestures and all gesture relations. The gestures won't be recognized outside of the root view, and relations only work between gestures mounted under the same root view. +If you don't provide anything to the `styles` prop, it will default to `flex: 1`. If you want to customize the styling of the root view, don't forget to also include `flex: 1` in the custom style, otherwise your app won't render anything. If you're unsure if one of your dependencies already renders `GestureHandlerRootView` on its own, don't worry and add one at the root anyway. In case of nested root views, Gesture Handler will only use the top-most one and ignore the nested ones. -:::tip -If you're using gesture handler in your component library, you may want to wrap your library's code in the `GestureHandlerRootView` component. This will avoid extra configuration for the user. +:::note +Remember that you need to wrap each screen that you use in your app with `GestureHandlerRootView` as with native navigation libraries each screen maps to a separate root view. It will not be enough to wrap the main screen only. ::: -:::tip +#### Using `GestureHandlerRootView` with third party libraries + If you're having trouble with gestures not working when inside a component provided by a third-party library, even though you've wrapped the entry point with ``, you can try adding another `` closer to the place the gestures are defined. This way, you can prevent Android from canceling relevant gestures when one of the native views tries to grab lock for delivering touch events. + +:::tip +If you're using Gesture Handler in your component library, you may want to wrap your library's code in the `GestureHandlerRootView` component. This will avoid extra configuration for the user. ::: ### 3. Platform specific setup @@ -77,7 +90,7 @@ npx expo prebuild #### Android -Setting up Gesture Handler on Android doesn't require any more steps. Keep in mind that if you want to use gestures in Modals you need to wrap Modal's content with `GestureHandlerRootView`: +Setting up Gesture Handler on Android doesn't require any more steps. Keep in mind that if you want to use gestures in [Modals](https://reactnative.dev/docs/modal) you need to wrap Modal's content with `GestureHandlerRootView`: ```jsx import { Modal } from 'react-native'; @@ -92,14 +105,12 @@ export function CustomModal({ children, ...rest }) { } ``` -##### Kotlin - Gesture Handler on Android is implemented in Kotlin. If you need to set a specific Kotlin version to be used by the library, set the `kotlinVersion` ext property in `android/build.gradle` file and RNGH will use that version: ```groovy buildscript { ext { - kotlinVersion = "1.6.21" + kotlinVersion = "2.1.20" } } ``` @@ -109,7 +120,15 @@ buildscript { While developing for iOS, make sure to install [pods](https://cocoapods.org/) first before running the app: ```bash -cd ios && pod install && cd .. +cd ios && bundle install && bundle exec pod install && cd .. +``` + +#### macOS + +While developing for macOS, make sure to install [pods](https://cocoapods.org/) first before running the app: + +```bash +cd macos && bundle install && bundle exec pod install && cd .. ``` #### Web @@ -125,6 +144,7 @@ import { Navigation } from 'react-native-navigation'; import FirstTabScreen from './FirstTabScreen'; import SecondTabScreen from './SecondTabScreen'; import PushedScreen from './PushedScreen'; + // register all screens of the app (including internal ones) export function registerScreens() { Navigation.registerComponent( @@ -164,5 +184,3 @@ export function registerScreens() { ``` You can check out [this example project](https://github.com/henrikra/nativeNavigationGestureHandler) to see this kind of set up in action. - -Remember that you need to wrap each screen that you use in your app with `GestureHandlerRootView` as with native navigation libraries each screen maps to a separate root view. It will not be enough to wrap the main screen only. diff --git a/packages/docs-gesture-handler/docs/fundamentals/introduction.md b/packages/docs-gesture-handler/docs/fundamentals/introduction.md index 2faf7e2eac..34f8707495 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/introduction.md +++ b/packages/docs-gesture-handler/docs/fundamentals/introduction.md @@ -10,9 +10,9 @@ Gesture Handler provides a declarative API exposing the native platform's touch The main benefits of using React Native Gesture Handler are: -- A way to use a platform's native touch handling system for recognizing gestures (like pinch, rotation, pan and a few others). -- The ability to define relations between gestures to ensure gestures, and possibly native components, will not conflict with each other. -- Mechanisms to use touchable components that run in native thread and follow platform default behavior; e.g. in the event they are in a scrollable component, turning into pressed state is slightly delayed to prevent it from highlighting when you fling. +- A way to use a platform's native touch handling system for recognizing gestures (like [pinch](/docs/gestures/use-pinch-gesture), [rotation](/docs/gestures/use-rotation-gesture), [pan](/docs/gestures/use-pan-gesture) and a few others). +- The ability to define [relations between gestures](/docs/fundamentals/gesture-composition) to ensure gestures, and possibly native components, will not conflict with each other. +- Mechanisms for components that run in native thread and follow platform default behavior, such as delaying the transition to a pressed state within scrollable components to prevent highlighting during a quick scroll or fling. - Close integration with [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) to process touch events on the UI thread. - Support for different input devices like touch screens, pens and mice. - Ability to include any native component into the Gesture Handler's touch system, making it work alongside your gestures. @@ -46,7 +46,7 @@ All PRs are welcome, but talk to us before you start working on something big. The easiest way to get started with contributing code is by: - Reviewing the list of [open issues](https://github.com/software-mansion/react-native-gesture-handler/issues) and trying to solve the one that seem approachable to you. -- Updating the [documentation](https://github.com/software-mansion/react-native-gesture-handler/blob/main/docs) whenever you see some information is unclear, missing or out of date. +- Updating the [documentation](https://github.com/software-mansion/react-native-gesture-handler/tree/main/packages/docs-gesture-handler) whenever you see some information is unclear, missing or out of date. Code is only one way how you can contribute. You may want to consider [replying on issues](https://github.com/software-mansion/react-native-gesture-handler/issues) if you know how to help. diff --git a/packages/docs-gesture-handler/docs/fundamentals/reanimated-interactions.mdx b/packages/docs-gesture-handler/docs/fundamentals/reanimated-interactions.mdx index b8f590d2eb..df2d223211 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/reanimated-interactions.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/reanimated-interactions.mdx @@ -7,7 +7,7 @@ sidebar_position: 4 import CollapsibleCode from '@site/src/components/CollapsibleCode'; -`GestureDetector` will decide whether to use [Reanimated](https://docs.swmansion.com/react-native-reanimated/) to process provided gestures based on their configuration. If any of the callbacks is a [worklet](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary/#worklet) and Reanimated is not explicitly [turned off](#disabling-reanimated), tools provided by the Reanimated will be utilized, bringing the ability to handle gestures synchronously on the main thread. +[`GestureDetector`](/docs/fundamentals/gesture-detectors) will decide whether to use [Reanimated](https://docs.swmansion.com/react-native-reanimated/) to process provided gestures based on their configuration. If any of the callbacks is a [worklet](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary/#worklet) and Reanimated is not explicitly [turned off](#disabling-reanimated), tools provided by the Reanimated will be utilized, bringing the ability to handle gestures synchronously on the main thread. ## Automatic [workletization](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary/#to-workletize) of gesture callbacks @@ -119,11 +119,11 @@ export default function App() { ## Disabling Reanimated -Gestures created with the hook API have `Reanimated` integration enabled by default, meaning all callbacks are executed on the UI thread. There are two methods available to disable this behavior for a specific gesture. +Gestures created with the hook API have Reanimated integration enabled by default, meaning all callbacks are executed on the UI thread. There are two methods available to disable this behavior for a specific gesture. ### disableReanimated -When `disableReanimated` is set to `true` in the gesture configuration, `Reanimated` integration will be completely turned off for that gesture throughout its entire lifecycle. This setting eliminates all interaction points with `Reanimated`, thereby reducing any potential overhead. Default value for this property is `false`. +When `disableReanimated` is set to `true` in the gesture configuration, Reanimated integration will be completely turned off for that gesture throughout its entire lifecycle. This setting eliminates all interaction points with Reanimated, thereby reducing any potential overhead. Default value for this property is `false`. This property cannot be changed dynamically during the gesture's lifecycle. diff --git a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx index fb31dc06db..cd310af2cd 100644 --- a/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx +++ b/packages/docs-gesture-handler/docs/fundamentals/state-manager.mdx @@ -7,11 +7,11 @@ sidebar_position: 6 import CollapsibleCode from '@site/src/components/CollapsibleCode'; -RNGH3 allows to manually control gestures lifecycle by using `GestureStateManager`. +RNGH3 allows to manually control [gestures lifecycle](/docs/under-the-hood/state) by using [`GestureStateManager`](#gesturestatemanager). ## State management -Manual state management is based on `handlerTag`. There are two ways of manual state control. +Manual state management is based on `handlerTag`. There are two ways of manual state control. Some gestures also support [`manualActivation`](#manualactivation) property, which blocks their automatic activation, even if they meet their activation criteria. ### Inside gesture definition @@ -125,7 +125,59 @@ const styles = StyleSheet.create({ }); `}/> -## Methods + +### manualActivation + +When `manualActivation` property is set to `true` on a gesture, it will not activate automatically even if its activation criteria are met. Use [`GestureStateManager`](#gesturestatemanager) to manipulate its state manually. + +In the example below, [`Pan`](/docs/gestures/use-pan-gesture) gesture will not activate by simply panning on the blue box - using `GestureStateManager` is necessary. + + { + console.log('Pan gesture activated'); + }, + manualActivation: true, + }); + + return ( + + + + + + ); +} + +const styles = { + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-around', + }, + box: { + width: 150, + height: 150, + backgroundColor: 'blue', + }, +}; +`}/> + +## GestureStateManager + +`GestureStateManager` provides methods to manipulate gesture's state imperatively. ### begin diff --git a/packages/docs-gesture-handler/docs/gestures/_examples/LongPressExample.tsx b/packages/docs-gesture-handler/docs/gestures/_examples/LongPressExample.tsx new file mode 100644 index 0000000000..7903707d81 --- /dev/null +++ b/packages/docs-gesture-handler/docs/gestures/_examples/LongPressExample.tsx @@ -0,0 +1,39 @@ +import { View, StyleSheet } from 'react-native'; +import { + GestureDetector, + GestureHandlerRootView, + useLongPressGesture, +} from 'react-native-gesture-handler'; + +export default function LongPressExample() { + const longPressGesture = useLongPressGesture({ + onDeactivate: (e, success) => { + if (success) { + console.log(`Long pressed for ${e.duration} ms!`); + } + }, + }); + + return ( + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-around', + }, + box: { + height: 120, + width: 120, + backgroundColor: '#b58df1', + borderRadius: 20, + marginBottom: 30, + }, +}); diff --git a/packages/docs-gesture-handler/docs/gestures/_examples/RotationExample.tsx b/packages/docs-gesture-handler/docs/gestures/_examples/RotationExample.tsx new file mode 100644 index 0000000000..8eed8ba47a --- /dev/null +++ b/packages/docs-gesture-handler/docs/gestures/_examples/RotationExample.tsx @@ -0,0 +1,47 @@ +import { StyleSheet } from 'react-native'; +import { + GestureDetector, + GestureHandlerRootView, + useRotationGesture, +} from 'react-native-gesture-handler'; +import Animated, { + useSharedValue, + useAnimatedStyle, +} from 'react-native-reanimated'; + +export default function App() { + const rotation = useSharedValue(1); + + const rotationGesture = useRotationGesture({ + onUpdate: (e) => { + rotation.value = savedRotation.value + e.rotation; + }, + }); + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ rotateZ: `${(rotation.value / Math.PI) * 180}deg` }], + })); + + return ( + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-around', + }, + box: { + height: 120, + width: 120, + backgroundColor: '#b58df1', + borderRadius: 20, + marginBottom: 30, + }, +}); diff --git a/packages/docs-gesture-handler/docs/gestures/_shared/base-continuous-gesture-config.md b/packages/docs-gesture-handler/docs/gestures/_shared/base-continuous-gesture-config.md index c4b1b4056e..c3143093a3 100644 --- a/packages/docs-gesture-handler/docs/gestures/_shared/base-continuous-gesture-config.md +++ b/packages/docs-gesture-handler/docs/gestures/_shared/base-continuous-gesture-config.md @@ -4,4 +4,4 @@ manualActivation: boolean | SharedValue; ``` -When `true` the handler will not activate by itself even if its activation criteria are met. Instead you can manipulate its state using [state manager](/docs/fundamentals/state-manager/). +When `true` the handler will not activate by itself even if its activation criteria are met. Instead you can manipulate its state using [`GestureStateManager`](/docs/fundamentals/state-manager). Example usage can be found [here](/docs/fundamentals/state-manager#manualactivation). diff --git a/packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-config.md b/packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-config.mdx similarity index 90% rename from packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-config.md rename to packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-config.mdx index 4c541a815c..65b43f0bfc 100644 --- a/packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-config.md +++ b/packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-config.mdx @@ -1,3 +1,5 @@ +import CollapsibleCode from '@site/src/components/CollapsibleCode'; + ### enabled ```ts @@ -21,11 +23,14 @@ Most handlers' `shouldCancelWhenOutside` property defaults to `false` except for ### hitSlop -```ts +; -``` -```ts type HitSlop = | number | null @@ -40,7 +45,7 @@ type HitSlop = | Record<'width' | 'right', number> | Record<'height' | 'top', number> | Record<'height' | 'bottom', number>; -``` +`}/> This parameter enables control over what part of the connected view area can be used to begin recognizing the gesture. When a negative number is provided the bounds of the view will reduce the area by the given number of points in each of the sides evenly. @@ -100,7 +105,7 @@ simultaneousWith: Gesture | Gesture[] Adds a gesture that should be recognized simultaneously with this one. -**IMPORTANT:** Note that this method only marks the relation between gestures, without [composing them](/docs/fundamentals/gesture-composition). [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector) will not recognize the `otherGestures` and it needs to be added to another detector in order to be recognized. +**IMPORTANT:** Note that this method only marks the relation between gestures, without [composing them](/docs/fundamentals/gesture-composition). [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector) will not recognize other gestures and it needs to be added to another detector in order to be recognized. ### requireToFail @@ -110,7 +115,7 @@ requireToFail: Gesture | Gesture[] Adds a relation requiring another gesture to fail, before this one can activate. -**IMPORTANT:** Note that this method only marks the relation between gestures, without [composing them](/docs/fundamentals/gesture-composition). [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector) will not recognize the `otherGestures` and it needs to be added to another detector in order to be recognized. +**IMPORTANT:** Note that this method only marks the relation between gestures, without [composing them](/docs/fundamentals/gesture-composition). [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector) will not recognize other gestures and it needs to be added to another detector in order to be recognized. ### block @@ -120,7 +125,7 @@ block: Gesture | Gesture[] Adds a relation that makes other gestures wait with activation until this gesture fails (or doesn't start at all). -**IMPORTANT:** Note that this method only marks the relation between gestures, without [composing them](/docs/fundamentals/gesture-composition).[`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector) will not recognize the `otherGestures` and it needs to be added to another detector in order to be recognized. +**IMPORTANT:** Note that this method only marks the relation between gestures, without [composing them](/docs/fundamentals/gesture-composition).[`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector) will not recognize other gestures and it needs to be added to another detector in order to be recognized. ### useAnimated diff --git a/packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-event-data.md b/packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-event-data.mdx similarity index 56% rename from packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-event-data.md rename to packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-event-data.mdx index 8b4266ecf9..8ed3c77583 100644 --- a/packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-event-data.md +++ b/packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-event-data.mdx @@ -1,3 +1,5 @@ +import CollapsibleCode from '@site/src/components/CollapsibleCode'; + ### numberOfPointers ```ts @@ -8,11 +10,14 @@ Represents the number of pointers (fingers) currently placed on the screen. ### pointerType -```ts + Indicates the type of pointer device in use. diff --git a/packages/docs-gesture-handler/docs/gestures/touch-events.md b/packages/docs-gesture-handler/docs/gestures/touch-events.md deleted file mode 100644 index c2244830ee..0000000000 --- a/packages/docs-gesture-handler/docs/gestures/touch-events.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -id: touch-events -title: Touch events -sidebar_label: Touch events -sidebar_position: 14 ---- - -### Touch event attributes: - -### `eventType` - -Type of the current event - whether the finger was placed on the screen, moved, lifted or cancelled. - -### `changedTouches` - -An array of objects where every object represents a single touch. Contains information only about the touches that were affected by the event i.e. those that were placed down, moved, lifted or cancelled. - -### `allTouches` - -An array of objects where every object represents a single touch. Contains information about all active touches. - -### `numberOfTouches` - -Number representing the count of currently active touches. - -:::caution -Don't rely on the order of items in the `touches` as it may change during the gesture, instead use the `id` attribute to track individual touches across events. -::: - -### PointerData attributes: - -### `id` - -A number representing id of the touch. It may be used to track the touch between events as the id will not change while it is being tracked. - -### `x` - -X coordinate of the current position of the touch relative to the view attached to the [`GestureDetector`](/docs/2.x/gestures/gesture-detector). Expressed in point units. - -### `y` - -Y coordinate of the current position of the touch relative to the view attached to the [`GestureDetector`](/docs/2.x/gestures/gesture-detector). Expressed in point units. - -### `absoluteX` - -X coordinate of the current position of the touch relative to the window. The value is expressed in point units. It is recommended to use it instead of [`x`](#x) in cases when the original view can be transformed as an effect of the gesture. - -### `absoluteY` - -Y coordinate of the current position of the touch relative to the window. The value is expressed in point units. It is recommended to use it instead of [`y`](#y) in cases when the original view can be transformed as an effect of the gesture. diff --git a/packages/docs-gesture-handler/docs/gestures/use-fling-gesture.mdx b/packages/docs-gesture-handler/docs/gestures/use-fling-gesture.mdx index 4aa6c21118..bbf1ca4d85 100644 --- a/packages/docs-gesture-handler/docs/gestures/use-fling-gesture.mdx +++ b/packages/docs-gesture-handler/docs/gestures/use-fling-gesture.mdx @@ -25,15 +25,14 @@ import FlingGestureBasicSrc from '!!raw-loader!@site/static/examples/FlingGestur /> -import BaseEventData from './\_shared/base-gesture-event-data.md'; -import BaseGestureConfig from './\_shared/base-gesture-config.md'; +import BaseEventData from './\_shared/base-gesture-event-data.mdx'; +import BaseGestureConfig from './\_shared/base-gesture-config.mdx'; import BaseGestureCallbacks from './\_shared/base-gesture-callbacks.mdx'; import SharedValueInfo from './\_shared/shared-value-info.md'; -A discrete gesture that activates when the movement is sufficiently long and fast. -Gesture activates when movement is sufficiently long and it does not take too much time. +The gesture that tracks quick, sufficiently long movement in specified [direction](#direction). When gesture gets activated it will end when finger is released. -The gesture will fail to recognize if the finger is lifted before being activated. +The gesture will fail if the finger is lifted before gesture could activate.
-import BaseEventData from './\_shared/base-gesture-event-data.md'; -import BaseGestureConfig from './\_shared/base-gesture-config.md'; +import BaseEventData from './\_shared/base-gesture-event-data.mdx'; +import BaseGestureConfig from './\_shared/base-gesture-config.mdx'; import BaseGestureCallbacks from './\_shared/base-gesture-callbacks.mdx'; import BaseContinuousGestureCallbacks from './\_shared/base-continuous-gesture-callbacks.mdx'; import SharedValueInfo from './\_shared/shared-value-info.md'; -A continuous gesture that can recognize hovering above the view it's attached to. The hover effect may be activated by moving a mouse or a stylus over the view. +Gesture that can recognize hovering above the view it's attached to. The hover effect may be activated by moving a mouse or a stylus over the view. -On iOS additional visual effects may be configured. +On iOS additional [visual effects](#effect-ios-only) may be configured.
+:::note +Don't rely on `Hover` gesture to continue after the mouse button is clicked or the stylus touches the screen. If you want to handle both cases, [compose](/docs/fundamentals/gesture-composition) it with [`Pan`](/docs/gestures/use-pan-gesture) gesture. +::: + ## Example -```jsx + ## Config @@ -89,17 +93,20 @@ const styles = StyleSheet.create({ ### effect (iOS only) -```ts +; -``` -```ts enum HoverEffect { NONE = 0, LIFT = 1, HIGHLIGHT = 2, } -``` +`}/> Visual effect applied to the view while the view is hovered. Defaults to `HoverEffect.None` diff --git a/packages/docs-gesture-handler/docs/gestures/use-long-press-gesture.mdx b/packages/docs-gesture-handler/docs/gestures/use-long-press-gesture.mdx index 811b2dd365..14f2f31660 100644 --- a/packages/docs-gesture-handler/docs/gestures/use-long-press-gesture.mdx +++ b/packages/docs-gesture-handler/docs/gestures/use-long-press-gesture.mdx @@ -25,14 +25,18 @@ import LongPressGestureBasicSrc from '!!raw-loader!@site/static/examples/LongPre /> -import BaseEventData from './\_shared/base-gesture-event-data.md'; -import BaseGestureConfig from './\_shared/base-gesture-config.md'; +import BaseEventData from './\_shared/base-gesture-event-data.mdx'; +import BaseGestureConfig from './\_shared/base-gesture-config.mdx'; import BaseGestureCallbacks from './\_shared/base-gesture-callbacks.mdx'; import SharedValueInfo from './\_shared/shared-value-info.md'; -A discrete gesture that activates when the corresponding view is pressed for a sufficiently long time. -This gesture's state will end immediately after the finger is released. -The gesture will fail to recognize a touch event if the finger is lifted before the minimum required time or if the finger is moved further than the allowable distance. +import CollapsibleCode from '@site/src/components/CollapsibleCode'; + +import LongPressExample from '!!raw-loader!./_examples/LongPressExample'; + +Gesture that activates when the corresponding view is pressed for a sufficiently long time. +This gesture will deactivate immediately after the finger is released. +The gesture will fail to recognize a touch event if the finger is lifted before the [minimum required time](#minduration) or if the finger is moved further than the [allowable distance](#maxdistance).