@uiw/react-native
Version:
UIW for React Native
257 lines (242 loc) • 6.9 kB
JavaScript
import React from 'react';
import { View, StyleSheet, Text, TouchableOpacity, Animated, TextInput } from 'react-native';
import Icon from '../Icon';
import RightButton from './RightButton';
export default class SearchInputBar extends React.Component {
inputRef = React.createRef();
moveLeft = new Animated.Value(0);
placeholderIcon = React.createRef();
placeholderAnimated = new Animated.Value(1);
buttonAnimated = new Animated.Value(this.props.buttonWidth ?? 70);
buttonAnimatedWidth = new Animated.Value(0);
constructor(props) {
super(props);
this.state = {
showIcon: false,
showIconLeft: 0
};
}
changeIconBoxStyle = flag => {
if ('_value' in this.buttonAnimatedWidth) {
const _value = this.buttonAnimatedWidth._value;
if (_value && !flag) {
return;
}
}
const {
buttonWidth = 70,
showActionButton
} = this.props;
Animated.timing(this.placeholderAnimated, {
toValue: flag ? 1 : 0,
duration: 50,
useNativeDriver: true
}).start();
Animated.timing(this.buttonAnimated, {
toValue: flag ? buttonWidth : 0,
duration: 150,
useNativeDriver: true
}).start(() => {});
this.buttonAnimatedWidth.setValue(flag ? 0 : buttonWidth);
this.placeholderIcon.current && this.placeholderIcon.current.measure((_frameOffsetX, _frameOffsetY, _width, _height, pageOffsetX, pageOffsetY) => {
const num = showActionButton ? 0 : buttonWidth;
Animated.timing(this.moveLeft, {
toValue: flag ? 0 : -pageOffsetX + 20 + num / 2 + 10,
duration: 300,
useNativeDriver: true
}).start(({
finished
}) => {
flag || this.inputRef.current && this.inputRef.current.focus();
});
});
};
onChangeText = value => {
const {
onChangeText = Function
} = this.props;
onChangeText(value);
if (!value && this.state.showIcon) {
this.setState({
showIcon: false
});
return;
}
if (value && !this.state.showIcon) {
this.inputRef.current && this.inputRef.current.measure((_frameOffsetX, _frameOffsetY, _width) => {
this.setState({
showIconLeft: _width - 40,
showIcon: true
});
});
}
};
componentDidMount() {
const {
value,
buttonWidth = 70,
showActionButton
} = this.props;
if (value) {
Animated.timing(this.placeholderAnimated, {
toValue: 1,
duration: 100,
useNativeDriver: true
}).start(() => {
this.changeIconBoxStyle(false);
this.inputRef.current && this.inputRef.current.measure((_frameOffsetX, _frameOffsetY, _width) => {
let num = showActionButton ? 0 : buttonWidth;
this.setState({
showIconLeft: _width - 40 - num,
showIcon: true
});
});
});
}
}
onClose = () => {
const {
onClear
} = this.props;
onClear?.();
this.setState({
showIcon: false
});
this.inputRef.current && this.inputRef.current.focus();
};
render() {
const {
showIcon,
showIconLeft
} = this.state;
const {
value,
onBlur,
placeholder = '请输入',
buttonWidth = 70,
showActionButton = false,
button,
actionName,
...other
} = this.props;
return <View style={styles.searchContainer}>
<View style={[styles.centerBox, {
justifyContent: 'space-between',
overflow: 'hidden'
}]}>
<TextInput {...other} ref={this.inputRef} style={[styles.searchInput, {
flex: 1
}]} onChangeText={this.onChangeText} value={value} onBlur={event => {
if (!value) {
this.changeIconBoxStyle(true);
}
onBlur?.(event);
}} />
<Animated.View style={showActionButton ? {} : {
transform: [{
translateX: this.buttonAnimated
}]
}}>
<Animated.View style={showActionButton ? {} : {
width: this.buttonAnimatedWidth
}}>
<RightButton width={buttonWidth} isShow actionName={actionName} {...button} />
</Animated.View>
</Animated.View>
</View>
<View style={[styles.iconBox, {
justifyContent: 'space-between',
overflow: 'hidden'
}]}>
<TouchableOpacity activeOpacity={1} style={[styles.centerBox, {
flex: 1
}]} onPress={() => {
this.changeIconBoxStyle(false);
}}>
<Animated.View style={[{
transform: [{
translateX: this.moveLeft
}]
}]}>
<View style={[styles.centerBox]} ref={this.placeholderIcon}>
<Icon name="search" size={18} color="#999" />
</View>
</Animated.View>
<Animated.View style={[{
transform: [{
translateX: this.moveLeft
}]
}]}>
<Animated.View style={[styles.centerBox, {
opacity: this.placeholderAnimated
}]}>
<Text style={styles.placeholderStyle}>{placeholder}</Text>
</Animated.View>
</Animated.View>
</TouchableOpacity>
<Animated.View style={showActionButton ? {} : {
transform: [{
translateX: this.buttonAnimated
}]
}}>
<Animated.View style={showActionButton ? {} : {
width: this.buttonAnimatedWidth
}}>
<RightButton width={buttonWidth} isShow actionName={actionName} {...button} />
</Animated.View>
</Animated.View>
</View>
{showIcon && <TouchableOpacity activeOpacity={1} style={[styles.closeStyle, styles.centerBox, {
left: showIconLeft
}]} onPress={this.onClose}>
<Icon name="close" size={18} color="#999" />
</TouchableOpacity>}
</View>;
}
}
const styles = StyleSheet.create({
searchContainer: {
paddingHorizontal: 20,
borderRadius: 5,
height: 40,
overflow: 'hidden'
},
iconBox: {
height: 40,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent',
position: 'relative',
top: -40,
zIndex: 1
},
centerBox: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center'
},
placeholderStyle: {
paddingLeft: 10,
fontSize: 18,
color: '#999'
},
searchInput: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
borderRadius: 20,
backgroundColor: '#f7f7f7',
fontSize: 18,
paddingHorizontal: 40
},
closeStyle: {
position: 'relative',
height: 40,
width: 40,
top: -80,
zIndex: 2
}
});