UNPKG

@uiw/react-native

Version:
127 lines (125 loc) 4.71 kB
import React, { useState, useRef, useEffect } from 'react'; import { View, StyleSheet, TouchableOpacity, Modal, Pressable, Animated } from 'react-native'; import Cloud from './Cloud'; import { getLocation, xLocation } from './utils'; export default function Tooltip(props) { const { width = 100, height = 20, children, title, toggleAction = 'onPress', backgroundColor = '#000000', borderRadius = 10, fadeAnim = [new Animated.Value(0), new Animated.Value(0)], refCloud = React.createRef(), triangle = 0, isStart = 'flex-start', isDown = false, ...other } = props; const refFollow = useRef(); const [state, setState] = useState({ modalVisible: false, cloudStyle: { left: 0, top: 0, }, followStyle: { width, height, }, modalViewStyle: {}, }); useEffect(() => { Animated.stagger(50, fadeAnim.map((item, index) => Animated.timing(item, { toValue: index === 0 ? 0.6 : 1, duration: 300, useNativeDriver: true, }))).start(({ finished }) => { props.onOpen && props.onOpen(); }); }, [props.onOpen]); const onOpen = () => { const { fadeAnim } = props; setState({ modalVisible: true }); refFollow.current && refFollow.current.measure((_frameOffsetX, _frameOffsetY, _width, _height, pageOffsetX, pageOffsetY) => { refCloud.current && refCloud.current.measure((_X, _Y, _W, _H, _PX, _PY) => { const cloudStyle = getLocation(_width, _height, pageOffsetX, pageOffsetY, _W, _H); let isDown = cloudStyle.isDown; let isStart = cloudStyle.isStart; let triangle = cloudStyle.triangle; setState({ modalViewStyle: { width: _width, height: _height, position: 'absolute', zIndex: 9999, left: pageOffsetX, top: pageOffsetY, }, cloudStyle: cloudStyle.style, }); }); }); }; const onClose = () => { Animated.stagger(50, fadeAnim .map((item, index) => Animated.timing(item, { toValue: 0, duration: 300, useNativeDriver: true, })) .reverse()).start(({ finished }) => { props.onClose && props.onClose(); }); setState({ ...state, modalVisible: false }); }; return (<View> <Modal testID="RNE__Tooltip__wrap" animationType="fade" // slide none fade transparent={true} visible={state.modalVisible} {...other}> <TouchableOpacity activeOpacity={1} style={styles.position} onPress={onClose}> <Animated.View style={[ styles.position, styles.backdrop, { opacity: fadeAnim[0], }, ]}></Animated.View> </TouchableOpacity> <View style={[styles.followView, { ...state.modalViewStyle }]}>{children}</View> <Animated.View style={[ styles.containerView, { opacity: fadeAnim[1], }, ]}> <View style={[styles.containerView, { ...state.cloudStyle }]} ref={refCloud}> <Cloud title={title} isDown={isDown} isStart={isStart} triangle={triangle} backgroundColor={backgroundColor} borderRadius={borderRadius}/> </View> </Animated.View> </Modal> <Pressable testID="RNE__Tooltip__pressable" {...{ [toggleAction]: onOpen }} delayLongPress={250}> <View style={[styles.followView, { ...state.followStyle }]} ref={refFollow}> {children} </View> </Pressable> </View>); } Tooltip.defaultProps = { fadeAnim: [new Animated.Value(0), new Animated.Value(0)], refFollow: React.createRef(), refCloud: React.createRef(), isDown: false, // 三角 上下 isStart: xLocation.start, // 三角 左 中 右 triangle: 0, // 三角 位置 }; const styles = StyleSheet.create({ position: { position: 'absolute', backgroundColor: 'transparent', top: 0, bottom: 0, left: 0, right: 0, zIndex: 9998, }, backdrop: { backgroundColor: '#fff', }, containerView: { position: 'absolute', zIndex: 9999, }, followView: { overflow: 'hidden', }, });