From 74da81c2236d5f04fff5aa634590062797bcca60 Mon Sep 17 00:00:00 2001 From: Nick Mallory Date: Mon, 13 Jul 2020 12:20:47 -0400 Subject: [PATCH 1/7] Fixed ripple showing behind button --- src/components/Ripple.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Ripple.tsx b/src/components/Ripple.tsx index 70f9844db..22766315e 100644 --- a/src/components/Ripple.tsx +++ b/src/components/Ripple.tsx @@ -213,10 +213,9 @@ const DEBOUNCE = 200; const styles = StyleSheet.create({ container: { ...StyleSheet.absoluteFillObject, - backgroundColor: 'transparent', overflow: 'hidden', + zIndex: 1, }, - ripple: { width: RADIUS * 2, height: RADIUS * 2, From 0a3bdb247a1f04426ce96e88d417d9aabe7858ed Mon Sep 17 00:00:00 2001 From: Nick Mallory Date: Mon, 13 Jul 2020 12:21:10 -0400 Subject: [PATCH 2/7] Consistent border radius for buttons --- src/components/Button.tsx | 2 +- src/components/ButtonMultiline.tsx | 2 +- src/components/ButtonSingleLine.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 30d92e513..35adfc922 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -71,7 +71,7 @@ export const Button = ({ const content = ( Date: Mon, 13 Jul 2020 13:06:11 -0400 Subject: [PATCH 3/7] Ripple component using RectButton instead of custom solution --- src/components/Button.tsx | 9 +- src/components/ButtonMultiline.tsx | 17 +- src/components/ButtonSingleLine.tsx | 12 +- src/components/InfoButton.tsx | 30 +++- src/components/Ripple.tsx | 243 ++++------------------------ src/components/TouchableIcon.tsx | 11 +- 6 files changed, 76 insertions(+), 246 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 35adfc922..1b66f49b3 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -14,8 +14,8 @@ import {Theme, palette} from 'shared/theme'; import {useI18n} from '@shopify/react-i18n'; import {Box} from './Box'; -import {Ripple} from './Ripple'; import {Icon, IconName} from './Icon'; +import {Ripple} from './Ripple'; export interface ButtonProps { text?: string; @@ -69,14 +69,15 @@ export const Button = ({ : {}; const externalArrowIcon = textColor === palette.white ? 'icon-external-arrow-light' : 'icon-external-arrow'; + const borderRadius = 4; const content = ( + {content} ); diff --git a/src/components/ButtonMultiline.tsx b/src/components/ButtonMultiline.tsx index ae75283cd..9cdb1b139 100644 --- a/src/components/ButtonMultiline.tsx +++ b/src/components/ButtonMultiline.tsx @@ -9,13 +9,15 @@ import { ViewStyle, ActivityIndicator, AccessibilityRole, + View, } from 'react-native'; import {Theme, palette} from 'shared/theme'; import {useI18n} from '@shopify/react-i18n'; +import {RectButton} from 'react-native-gesture-handler'; import {Box} from './Box'; -import {Ripple} from './Ripple'; import {Icon} from './Icon'; +import {Ripple} from './Ripple'; export interface ButtonMultilineProps { text?: string; @@ -59,13 +61,18 @@ export const ButtonMultiline = ({ } : {}; const externalArrowIcon = textColor === palette.white ? 'icon-external-arrow-light' : 'icon-external-arrow'; - + const borderRadius = 4; const content = ( + {content} ); diff --git a/src/components/ButtonSingleLine.tsx b/src/components/ButtonSingleLine.tsx index b0b533899..acbfe9e61 100644 --- a/src/components/ButtonSingleLine.tsx +++ b/src/components/ButtonSingleLine.tsx @@ -8,14 +8,16 @@ import { ViewStyle, ActivityIndicator, AccessibilityRole, + View, } from 'react-native'; import {Theme, palette} from 'shared/theme'; import {useI18n} from '@shopify/react-i18n'; +import {RectButton} from 'react-native-gesture-handler'; import {Box} from './Box'; -import {Ripple} from './Ripple'; import {Icon, IconName} from './Icon'; import {Text} from './Text'; +import {Ripple} from './Ripple'; export interface ButtonSingleLineProps { text?: string; @@ -59,14 +61,14 @@ export const ButtonSingleLine = ({ } : {}; const externalArrowIcon = textColor === palette.white ? 'icon-external-arrow-light' : 'icon-external-arrow'; - + const borderRadius = 4; const content = ( + {content} ); diff --git a/src/components/InfoButton.tsx b/src/components/InfoButton.tsx index 934e27a38..dce4fe423 100644 --- a/src/components/InfoButton.tsx +++ b/src/components/InfoButton.tsx @@ -8,9 +8,11 @@ import { ViewStyle, ActivityIndicator, AccessibilityRole, + View, } from 'react-native'; import {Theme, palette} from 'shared/theme'; import {useI18n} from '@shopify/react-i18n'; +import {RectButton} from 'react-native-gesture-handler'; import {Box} from './Box'; import {Ripple} from './Ripple'; @@ -58,13 +60,18 @@ export const InfoButton = ({ } : {}; const externalArrowIcon = textColor === palette.white ? 'icon-external-arrow-light' : 'icon-external-arrow'; - + const borderRadius = 10; const content = ( - {content} - + + + {content} + + ); } return ( diff --git a/src/components/Ripple.tsx b/src/components/Ripple.tsx index 22766315e..bcff3496e 100644 --- a/src/components/Ripple.tsx +++ b/src/components/Ripple.tsx @@ -1,226 +1,37 @@ -import React, {useCallback, useMemo, useRef, useState} from 'react'; -import { - Animated, - Easing, - GestureResponderEvent, - I18nManager, - LayoutChangeEvent, - StyleSheet, - TouchableWithoutFeedback, - TouchableWithoutFeedbackProps, - View, -} from 'react-native'; - -interface Props { - rippleColor?: string; - rippleCentered?: boolean; - rippleOpacity?: number; - rippleFades?: boolean; - rippleExpandDuration?: number; - rippleFadeDuration?: number; - rippleSize?: number; - rippleContainerBorderRadius?: number; - children?: React.ReactNode; -} - -interface AnimatedRipple { - id: number; - locationX: number; - locationY: number; - radius: number; - expandAnimatedValue: Animated.Value; - fadeAnimatedValue: Animated.Value; - timestamp: number; +import React from 'react'; +import {View, ViewProps} from 'react-native'; +import {RectButton} from 'react-native-gesture-handler'; + +interface RippleProps { + children: React.ReactNode; + disabled?: boolean; + backgroundColor?: string; + borderRadius?: number; + onPress: () => void; } -export type RippleProps = Props & TouchableWithoutFeedbackProps; - export const Ripple = ({ children, - disabled, - rippleColor = 'rgb(0, 0, 0)', - rippleCentered = false, - rippleOpacity = 0.3, - rippleExpandDuration = 500, - rippleFadeDuration = 200, - rippleContainerBorderRadius = 0, - rippleSize = 0, - ...touchableWithoutFeedbackProps -}: RippleProps) => { - const [width, setWidth] = useState(0); - const [height, setHeight] = useState(0); - const uuid = useRef(0); - const [ripples, setRipples] = useState([]); - const [fadings, setFadings] = useState([]); - - const startFade = useCallback( - (ripple: AnimatedRipple, duration: number) => { - if (fadings.indexOf(ripple.id) >= 0) { - return; - } - setFadings([...fadings, ripple.id]); - Animated.timing(ripple.fadeAnimatedValue, { - toValue: 1, - easing: Easing.out(Easing.ease), - duration, - useNativeDriver: true, - }).start(() => { - setRipples(ripples.filter(item => item !== ripple)); - }); - }, - [fadings, ripples], - ); - - const startExpand = useCallback( - (event: GestureResponderEvent) => { - if (!width || !height) { - return; - } - - const timestamp = Date.now(); - if (ripples.length > 0 && timestamp < ripples[ripples.length - 1].timestamp + DEBOUNCE) { - return; - } - - const w2 = 0.5 * width; - const h2 = 0.5 * height; - - const {locationX, locationY} = rippleCentered ? {locationX: w2, locationY: h2} : event.nativeEvent; - - const offsetX = Math.abs(w2 - locationX); - const offsetY = Math.abs(h2 - locationY); - - const radius = rippleSize > 0 ? 0.5 * rippleSize : Math.sqrt((w2 + offsetX) ** 2 + (h2 + offsetY) ** 2); - - const id = uuid.current; - uuid.current += 1; - - const ripple: AnimatedRipple = { - id, - locationX, - locationY, - radius, - timestamp, - expandAnimatedValue: new Animated.Value(0), - fadeAnimatedValue: new Animated.Value(0), - }; - - Animated.timing(ripple.expandAnimatedValue, { - toValue: 1, - easing: Easing.out(Easing.ease), - duration: rippleExpandDuration, - useNativeDriver: true, - }).start(); - - setRipples(ripples.concat(ripple)); - }, - [height, rippleCentered, rippleExpandDuration, rippleSize, ripples, width], - ); - - const onLayout = useCallback( - (event: LayoutChangeEvent) => { - const { - nativeEvent: { - layout: {height, width}, - }, - } = event; - setWidth(width); - setHeight(height); - touchableWithoutFeedbackProps.onLayout?.(event); - }, - [touchableWithoutFeedbackProps.onLayout], - ); - - const onPressIn = useCallback( - (event: GestureResponderEvent) => { - startExpand(event); - touchableWithoutFeedbackProps.onPressIn?.(event); - }, - [startExpand, touchableWithoutFeedbackProps.onPressIn], - ); - - const onPressOut = useCallback( - (event: GestureResponderEvent) => { - ripples.forEach(ripple => startFade(ripple, rippleFadeDuration + rippleExpandDuration / 2)); - touchableWithoutFeedbackProps.onPressOut?.(event); - }, - [rippleExpandDuration, rippleFadeDuration, ripples, startFade, touchableWithoutFeedbackProps.onPressOut], - ); - - const onPress = useCallback( - (event: GestureResponderEvent) => { - requestAnimationFrame(() => { - touchableWithoutFeedbackProps.onPress?.(event); - }); - }, - [touchableWithoutFeedbackProps.onPress], - ); - - const renderRipple = useCallback( - ({locationX, locationY, radius, id, expandAnimatedValue, fadeAnimatedValue}: AnimatedRipple) => { - const rippleStyle = { - top: locationY - RADIUS, - [I18nManager.isRTL ? 'right' : 'left']: locationX - RADIUS, - backgroundColor: rippleColor, - transform: [ - { - scale: expandAnimatedValue.interpolate({ - inputRange: [0, 1], - outputRange: [0.5 / RADIUS, radius / RADIUS], - }), - }, - ], - opacity: fadeAnimatedValue.interpolate({ - inputRange: [0, 1], - outputRange: [rippleOpacity, 0], - }), - }; - return ; - }, - [rippleColor, rippleOpacity], - ); - - const style = useMemo( - () => [ - styles.container, - { - borderRadius: rippleContainerBorderRadius, - }, - ], - [rippleContainerBorderRadius], - ); - + disabled = false, + backgroundColor = 'transparent', + borderRadius = 4, + onPress, + ...props +}: RippleProps & ViewProps) => { return ( - - - {ripples.map(renderRipple)} + {children} - - + + ); }; - -const RADIUS = 8; -const DEBOUNCE = 200; - -const styles = StyleSheet.create({ - container: { - ...StyleSheet.absoluteFillObject, - overflow: 'hidden', - zIndex: 1, - }, - ripple: { - width: RADIUS * 2, - height: RADIUS * 2, - borderRadius: RADIUS, - overflow: 'hidden', - position: 'absolute', - }, -}); diff --git a/src/components/TouchableIcon.tsx b/src/components/TouchableIcon.tsx index 06010462e..7d7674f32 100644 --- a/src/components/TouchableIcon.tsx +++ b/src/components/TouchableIcon.tsx @@ -26,16 +26,7 @@ export const TouchableIcon = ({iconName, iconSize, containerSize = 66, label, on ); if (Platform.OS === 'android') { - return ( - - {content} - - ); + return {content}; } return ( From b5ab7ea94cd9216dd728df3d97d1cf4ea0cc7163 Mon Sep 17 00:00:00 2001 From: Nick Mallory Date: Mon, 13 Jul 2020 13:08:27 -0400 Subject: [PATCH 4/7] Added accessibilityProps to all Ripple components --- src/components/Button.tsx | 8 +++++++- src/components/ButtonMultiline.tsx | 8 +++++++- src/components/ButtonSingleLine.tsx | 8 +++++++- src/components/TouchableIcon.tsx | 6 +++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 1b66f49b3..ca2eeeb14 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -116,7 +116,13 @@ export const Button = ({ if (Platform.OS === 'android') { return ( - + {content} ); diff --git a/src/components/ButtonMultiline.tsx b/src/components/ButtonMultiline.tsx index 9cdb1b139..47f59ae64 100644 --- a/src/components/ButtonMultiline.tsx +++ b/src/components/ButtonMultiline.tsx @@ -123,7 +123,13 @@ export const ButtonMultiline = ({ if (Platform.OS === 'android') { return ( - + {content} ); diff --git a/src/components/ButtonSingleLine.tsx b/src/components/ButtonSingleLine.tsx index acbfe9e61..494b2eb91 100644 --- a/src/components/ButtonSingleLine.tsx +++ b/src/components/ButtonSingleLine.tsx @@ -122,7 +122,13 @@ export const ButtonSingleLine = ({ if (Platform.OS === 'android') { return ( - + {content} ); diff --git a/src/components/TouchableIcon.tsx b/src/components/TouchableIcon.tsx index 7d7674f32..0be0ebbed 100644 --- a/src/components/TouchableIcon.tsx +++ b/src/components/TouchableIcon.tsx @@ -26,7 +26,11 @@ export const TouchableIcon = ({iconName, iconSize, containerSize = 66, label, on ); if (Platform.OS === 'android') { - return {content}; + return ( + + {content} + + ); } return ( From fc6d80ac4408b52d7d39d4c27b2d737745be849e Mon Sep 17 00:00:00 2001 From: Nick Mallory Date: Mon, 13 Jul 2020 13:15:34 -0400 Subject: [PATCH 5/7] Use Ripple in InfoButton --- src/components/ButtonMultiline.tsx | 2 -- src/components/ButtonSingleLine.tsx | 2 -- src/components/InfoButton.tsx | 19 +++++++------------ 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/components/ButtonMultiline.tsx b/src/components/ButtonMultiline.tsx index 47f59ae64..7fa8c18b0 100644 --- a/src/components/ButtonMultiline.tsx +++ b/src/components/ButtonMultiline.tsx @@ -9,11 +9,9 @@ import { ViewStyle, ActivityIndicator, AccessibilityRole, - View, } from 'react-native'; import {Theme, palette} from 'shared/theme'; import {useI18n} from '@shopify/react-i18n'; -import {RectButton} from 'react-native-gesture-handler'; import {Box} from './Box'; import {Icon} from './Icon'; diff --git a/src/components/ButtonSingleLine.tsx b/src/components/ButtonSingleLine.tsx index 494b2eb91..c48aa191c 100644 --- a/src/components/ButtonSingleLine.tsx +++ b/src/components/ButtonSingleLine.tsx @@ -8,11 +8,9 @@ import { ViewStyle, ActivityIndicator, AccessibilityRole, - View, } from 'react-native'; import {Theme, palette} from 'shared/theme'; import {useI18n} from '@shopify/react-i18n'; -import {RectButton} from 'react-native-gesture-handler'; import {Box} from './Box'; import {Icon, IconName} from './Icon'; diff --git a/src/components/InfoButton.tsx b/src/components/InfoButton.tsx index dce4fe423..60507b653 100644 --- a/src/components/InfoButton.tsx +++ b/src/components/InfoButton.tsx @@ -103,20 +103,15 @@ export const InfoButton = ({ if (Platform.OS === 'android') { return ( - - - {content} - - + {content} + ); } return ( From 75aa4f01b87ae689b9c71617048bb9af5ad5193f Mon Sep 17 00:00:00 2001 From: Nick Mallory Date: Mon, 13 Jul 2020 13:18:02 -0400 Subject: [PATCH 6/7] lint --- src/components/InfoButton.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/InfoButton.tsx b/src/components/InfoButton.tsx index 60507b653..89efda7e1 100644 --- a/src/components/InfoButton.tsx +++ b/src/components/InfoButton.tsx @@ -8,11 +8,9 @@ import { ViewStyle, ActivityIndicator, AccessibilityRole, - View, } from 'react-native'; import {Theme, palette} from 'shared/theme'; import {useI18n} from '@shopify/react-i18n'; -import {RectButton} from 'react-native-gesture-handler'; import {Box} from './Box'; import {Ripple} from './Ripple'; From d34090832e5a4e6f4dc28adedc3fb4ea7b7383a2 Mon Sep 17 00:00:00 2001 From: Nick Mallory Date: Mon, 13 Jul 2020 15:11:29 -0400 Subject: [PATCH 7/7] Updated border radius on all button components --- src/components/Button.tsx | 2 +- src/components/ButtonMultiline.tsx | 2 +- src/components/ButtonSingleLine.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index ca2eeeb14..bbc3f1f60 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -69,7 +69,7 @@ export const Button = ({ : {}; const externalArrowIcon = textColor === palette.white ? 'icon-external-arrow-light' : 'icon-external-arrow'; - const borderRadius = 4; + const borderRadius = 5; const content = (