/** * Copyright (c) 2019 Paul Armstrong */ import * as Theme from '../theme'; import React from 'react'; import { StyleProp, StyleSheet, TouchableOpacity, TouchableOpacityProps, View, ViewStyle } from 'react-native'; interface Props { children: React.ReactElement; disabled?: boolean; nativeID?: string; rippleColor?: string | 'primary' | 'secondary'; style?: StyleProp; } const Ripple = (props: Props & TouchableOpacityProps): React.ReactElement => { const [location, setLocation] = React.useState({ x: 0, y: 0 }); const [size, setSize] = React.useState({ width: 0, height: 0 }); const [showRipple, setRippleVisibility] = React.useState(false); const rootRef: React.RefObject = React.useRef(null); const { children, disabled, rippleColor = 'rgba(0,0,0,0.2)', onPressIn, onPressOut, style, ...otherProps } = props; const handleLayout = React.useCallback((event): void => { const { width, height } = event.nativeEvent.layout; setSize({ width, height }); }, []); const handlePressIn = React.useCallback( (event): void => { const { locationX, locationY } = event.nativeEvent; if (!disabled) { setRippleVisibility(true); setLocation({ x: Math.floor(locationX - window.scrollX), y: Math.floor(locationY - window.scrollY), }); if (onPressIn) { onPressIn(event); } } }, [disabled, onPressIn] ); const handlePressOut = React.useCallback( (event): void => { if (disabled) { return; } setTimeout(() => { rootRef.current && setRippleVisibility(false); }, 400); if (onPressOut) { onPressOut(event); } }, [disabled, onPressOut, setRippleVisibility] ); const maxSide = Math.max(size.width, size.height); const sizeMidpoint = maxSide / 2; const offset = Math.max(Math.abs(location.x - sizeMidpoint), Math.abs(location.y - sizeMidpoint)); const finalSize = maxSide + offset * 2.5; return ( {children} ); }; const styles = StyleSheet.create({ wrapper: { display: 'inline-flex', }, root: { display: 'inline-flex', overflow: 'hidden', position: 'relative', }, ripple: { position: 'absolute', borderRadius: 9999, top: 0, left: 0, width: 0, height: 0, zIndex: -1, opacity: 0, transitionProperty: 'transform, opacity', transitionDuration: '0.4s', transitionTimingFunction: Theme.MotionTiming.Standard, transformOrigin: '50% 50%', transform: [{ scale: 0 }], }, adjuster: { display: 'block', paddingTop: '100%', height: 0, width: 0, }, animateIn: { opacity: 1, transform: [{ scale: 1 }], }, animateOut: { animationKeyframes: [{ from: { opacity: 1 }, to: { opacity: 0 } }], animationDuration: '0.2s', animationIterationCount: 1, transitionDelay: '0.2s', transitionDuration: '0s', width: 0, height: 0, transform: [{ scale: 0 }], opacity: 0, }, }); export default Ripple;