Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/components/TouchableRipple/Pressable.tsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
import type * as React from 'react';
import type {
PressableProps as PressableNativeProps,
StyleProp,
View,
ViewStyle,
} from 'react-native';
import { Pressable as PressableNative } from 'react-native';

// This component is added to support type-safe hover and focus states on web
// https://necolas.github.io/react-native-web/docs/pressable/

export type PressableStateCallbackType = {
pressed: boolean;
focused: boolean;
hovered: boolean;
};

export type PressableProps = Omit<
PressableNativeProps,
'children' | 'style'
> & {
children:
| React.ReactNode
| ((state: PressableStateCallbackType) => React.ReactNode)
| undefined;
style?:
| StyleProp<ViewStyle>
| ((state: PressableStateCallbackType) => StyleProp<ViewStyle>)
| undefined;
};

export const Pressable: React.ForwardRefExoticComponent<
PressableProps & React.RefAttributes<View>
> = PressableNative as any;
6 changes: 4 additions & 2 deletions src/components/TouchableRipple/TouchableRipple.native.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,18 +5,20 @@ import {
Platform,
ViewStyle,
StyleSheet,
Pressable,
GestureResponderEvent,
} from 'react-native';

import { useInternalTheme } from '../../core/theming';
import type { InternalTheme } from '../../types';
import type { PressableProps } from './Pressable';
import { Pressable } from './Pressable';
import type { ThemeProp } from '../../types';
import { getTouchableRippleColors } from './utils';

const ANDROID_VERSION_LOLLIPOP = 21;
const ANDROID_VERSION_PIE = 28;

export type Props = React.ComponentProps<typeof Pressable> & {
type Props = PressableProps & {
borderless?: boolean;
background?: PressableAndroidRippleConfig;
centered?: boolean;
Expand Down
46 changes: 34 additions & 12 deletions src/components/TouchableRipple/TouchableRipple.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,17 +2,20 @@ import * as React from 'react';
import {
GestureResponderEvent,
Platform,
Pressable,
StyleProp,
StyleSheet,
ViewStyle,
} from 'react-native';

import Color from 'color';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
importColorfrom'color';
importcolorfrom'color';


import { useInternalTheme } from '../../core/theming';
import type { ThemeProp } from '../../types';
import type { PressableProps, PressableStateCallbackType } from './Pressable';
import { Pressable } from './Pressable';
import { getTouchableRippleColors } from './utils';

export type Props = React.ComponentPropsWithRef<typeof Pressable> & {
export type Props = PressableProps & {
/**
* Whether to render the ripple outside the view bounds.
*/
Expand DownExpand Up@@ -49,8 +52,13 @@ export type Props = React.ComponentPropsWithRef<typeof Pressable> & {
/**
* Content of the `TouchableRipple`.
*/
children: React.ReactNode;
style?: StyleProp<ViewStyle>;
children:
| ((state: PressableStateCallbackType) => React.ReactNode)
| React.ReactNode;
style?:
| StyleProp<ViewStyle>
| ((state: PressableStateCallbackType) => StyleProp<ViewStyle>)
| undefined;
/**
* @optional
*/
Expand DownExpand Up@@ -100,16 +108,16 @@ const TouchableRipple = ({
...rest
}: Props) => {
const theme = useInternalTheme(themeOverrides);
const { calculatedRippleColor } = getTouchableRippleColors({
theme,
rippleColor,
});
const hoverColor = Color(calculatedRippleColor).fade(0.5).rgb().string();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
consthoverColor=Color(calculatedRippleColor).fade(0.5).rgb().string();
consthoverColor=color(calculatedRippleColor).fade(0.5).rgb().string();

const handlePressIn = (e: any) => {
const { centered, onPressIn } = rest;

onPressIn?.(e);

const { calculatedRippleColor } = getTouchableRippleColors({
theme,
rippleColor,
});

const button = e.currentTarget;
const style = window.getComputedStyle(button);
const dimensions = button.getBoundingClientRect();
Expand DownExpand Up@@ -236,9 +244,20 @@ const TouchableRipple = ({
onPressIn={handlePressIn}
onPressOut={handlePressOut}
disabled={disabled}
style={[styles.touchable, borderless && styles.borderless, style]}
style={(state) => [
styles.touchable,
borderless && styles.borderless,
// focused state is not ready yet: https://github.com/necolas/react-native-web/issues/1849
// state.focused && { backgroundColor: ___ },
state.hovered && { backgroundColor: hoverColor },
typeof style === 'function' ? style(state) : style,
]}
>
{React.Children.only(children)}
{(state) =>
React.Children.only(
typeof children === 'function' ? children(state) : children
)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit test for introduced changes in styles and rendering the children.

</Pressable>
);
};
Expand All@@ -251,7 +270,10 @@ TouchableRipple.supported = true;
const styles = StyleSheet.create({
touchable: {
position: 'relative',
...(Platform.OS === 'web' && { cursor: 'pointer' }),
...(Platform.OS === 'web' && {
cursor: 'pointer',
transition: '150ms background-color',
}),
},
borderless: {
overflow: 'hidden',
Expand Down