@uiw/react-native
Version:
UIW for React Native
127 lines (125 loc) • 4.71 kB
JavaScript
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',
},
});