Files
react-native/Libraries/Components/Pressable/useAndroidRippleForView.js
T
Nishan Bende 0823f299e5 Foreground ripple support in Pressable (#31632)
Summary:
This PR aims to enable support for foreground ripple in Pressable. This makes it possible to show ripple on top of custom child components like Image as shown in the below example.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[Android] [Added] - Support for foreground ripple in Pressable

Pull Request resolved: https://github.com/facebook/react-native/pull/31632

Test Plan:
- Pass property useForeground: true in android_ripple config to verify the changes.

https://user-images.githubusercontent.com/23293248/120111371-4cecbf00-c18f-11eb-8acb-d10718d5483c.mov

Reviewed By: kacieb

Differential Revision: D28926493

Pulled By: yungsters

fbshipit-source-id: 12a6ba71a7dc6ed60fbaeb651f015cace38e03b1
2021-06-07 17:07:58 -07:00

106 lines
3.0 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import invariant from 'invariant';
import {Commands} from '../View/ViewNativeComponent';
import type {ColorValue} from '../../StyleSheet/StyleSheet';
import type {PressEvent} from '../../Types/CoreEventTypes';
import {Platform, View, processColor} from 'react-native';
import * as React from 'react';
import {useMemo} from 'react';
type NativeBackgroundProp = $ReadOnly<{|
type: 'RippleAndroid',
color: ?number,
borderless: boolean,
rippleRadius: ?number,
|}>;
export type RippleConfig = {|
color?: ColorValue,
borderless?: boolean,
radius?: number,
foreground?: boolean,
|};
/**
* Provides the event handlers and props for configuring the ripple effect on
* supported versions of Android.
*/
export default function useAndroidRippleForView(
rippleConfig: ?RippleConfig,
viewRef: {|current: null | React.ElementRef<typeof View>|},
): ?$ReadOnly<{|
onPressIn: (event: PressEvent) => void,
onPressMove: (event: PressEvent) => void,
onPressOut: (event: PressEvent) => void,
viewProps:
| $ReadOnly<{|nativeBackgroundAndroid: NativeBackgroundProp|}>
| $ReadOnly<{|nativeForegroundAndroid: NativeBackgroundProp|}>,
|}> {
const {color, borderless, radius, foreground} = rippleConfig ?? {};
return useMemo(() => {
if (
Platform.OS === 'android' &&
Platform.Version >= 21 &&
(color != null || borderless != null || radius != null)
) {
const processedColor = processColor(color);
invariant(
processedColor == null || typeof processedColor === 'number',
'Unexpected color given for Ripple color',
);
const nativeRippleValue = {
type: 'RippleAndroid',
color: processedColor,
borderless: borderless === true,
rippleRadius: radius,
};
return {
viewProps:
foreground === true
? {nativeForegroundAndroid: nativeRippleValue}
: {nativeBackgroundAndroid: nativeRippleValue},
onPressIn(event: PressEvent): void {
const view = viewRef.current;
if (view != null) {
Commands.setPressed(view, true);
Commands.hotspotUpdate(
view,
event.nativeEvent.locationX ?? 0,
event.nativeEvent.locationY ?? 0,
);
}
},
onPressMove(event: PressEvent): void {
const view = viewRef.current;
if (view != null) {
Commands.hotspotUpdate(
view,
event.nativeEvent.locationX ?? 0,
event.nativeEvent.locationY ?? 0,
);
}
},
onPressOut(event: PressEvent): void {
const view = viewRef.current;
if (view != null) {
Commands.setPressed(view, false);
}
},
};
}
return null;
}, [borderless, color, foreground, radius, viewRef]);
}