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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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
import Color from 'color';
import color from '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 Down Expand 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 Down Expand 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
const hoverColor = Color(calculatedRippleColor).fade(0.5).rgb().string();
const hoverColor = 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 Down Expand 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