mh-rn-component
Version:
171 lines (167 loc) • 4.75 kB
Flow
import React, { useState, useEffect } from 'react'
import { ScrollView, View, Text, TextInput, TextStyle, ViewStyle, StyleProp, StyleSheet, TouchableWithoutFeedback, TextInputProps } from 'react-native';
import { FieldRefType } from '../types'
import Icon from '../Icon'
import renderNode from '../helpers/renderNode'
/**
* todo 要怎么定义字体大小?
* todo 报错了 React.ComponentPropsWithRef<typeof TextInput> &
*/
interface Props extends TextInputProps {
type?: string,
disabled?: boolean;
disabledInputStyle?: StyleProp<ViewStyle>;
label?: string;
// todo 字体大小 这个字段是否需要 我认为label只需要简单提供就好了 所以不用太精细
labelFontSize?: number,
labelStyle?: StyleProp<ViewStyle>;
style?: StyleProp<ViewStyle>;
rightIcon?: string | React.ReactElement<{}>
onRightIconClick?: () => void;
clearable?: boolean;
clearIcon?: string,//清除图标的icon name
clearIconColor?: string//清除图标的icon 颜色
onInput?: (value: string) => void
border?: boolean//是否显示边框
borderStyle?: StyleProp<ViewStyle>;
maxlength?: number
showWordLimit?: boolean //是否显示字数统计
fieldStyle?: StyleProp<ViewStyle>;
}
type TextInputHandles = FieldRefType
const Field = React.forwardRef<TextInputHandles, Props>(({
onRightIconClick,
clearable,
style,
type,
border = true,
...rest
}: Props, ref) => {
/**
* 传递ref节点添加方法
*/
const root = React.useRef<TextInput>(null);
React.useImperativeHandle(ref, () => {
const input = root.current;
if (input) {
return {
focus: () => input.focus(),
clear: () => input.clear(),
// setNativeProps: (args: TextInputProps) => input.setNativeProps(args),
isFocused: () => input.isFocused(),
blur: () => input.blur(),
};
}
const noop = () => {
throw new Error('TextInput is not available');
};
return {
focus: noop,
clear: noop,
blur: noop,
isFocused: noop
};
});
const clearableHandel = () => {
rest?.onInput && rest?.onInput("")
root.current?.clear()
}
/**
* [Props]type
* 输入框形态
* 软键盘形态
*/
const keyboardType = (type === 'number' ? 'numeric' : 'default')
return (
<View style={StyleSheet.flatten([
styles.container,
{ paddingBottom: rest.showWordLimit ? 20 : 8 },
rest.disabled && styles.disabledInput,
rest.disabled && rest.disabledInputStyle,
rest.fieldStyle])}>
{rest.label && (
<View style={[{ paddingRight: 8 }, rest.labelStyle]}>
<Text style={{ fontSize: rest.labelFontSize || 16 }}>{rest.label}:</Text>
</View>)
}
<View style={
StyleSheet.flatten([
styles.inputMain,
border && styles.border,
rest.borderStyle && rest.borderStyle])
}>
<TextInput
ref={root}
style={StyleSheet.flatten([
styles.input,
type === 'textarea' && styles.textarea,
style
])}
underlineColorAndroid="transparent"
editable={!rest.disabled}
secureTextEntry={type === 'password'}
keyboardType={keyboardType}
onChangeText={rest.onInput}
multiline={type === 'textarea'}
{...rest}
/>
{clearable && (
<Text style={{ display: rest.value ? "flex" : "none" }} onPress={clearableHandel}>
<Icon name={rest.clearIcon || "closecircle"} color={rest.clearIconColor || "#333"}></Icon>
</Text>
)}
{rest.rightIcon && (
<TouchableWithoutFeedback onPress={onRightIconClick}>
<View>{renderNode(Icon, rest.rightIcon)}</View>
</TouchableWithoutFeedback>)
}
</View>
{
(rest.showWordLimit && rest.maxlength) && <View style={styles.showWordLimit}>
<Text>{rest.value?.length}/{rest.maxlength}</Text>
</View>
}
{/* 自定义右边内容 */}
</View>
)
})
const styles = StyleSheet.create({
container: {
width: "100%",
flexDirection: 'row',
alignItems: 'center',
padding: 8,
backgroundColor: '#fff',
position: "relative",
},
inputMain: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
paddingLeft: 8,
paddingRight: 8,
},
border: {
borderWidth: 1,
borderColor: "#ccc",
borderRadius: 6,
},
input: {
flex: 1,
fontSize: 16,
minHeight: 40,
},
textarea: {
minHeight: 80,
textAlignVertical: 'top'
},
disabledInput: {
opacity: 0.5,
},
showWordLimit: {
position: "absolute",
right: 12,
bottom: 0
}
});
export default Field