react-native-tags-input
Version:
Input component for React Native to add and remove tags.
314 lines (287 loc) • 7.81 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import {
Text,
View,
TextInput,
StyleSheet,
Image,
TouchableOpacity,
ViewPropTypes
} from 'react-native';
class Tags extends React.Component {
focus() {
this.input.focus();
}
blur() {
this.input.blur();
}
clear() {
this.input.clear();
}
isFocused() {
return this.input.isFocused();
}
setNativeProps(nativeProps) {
this.input.setNativeProps(nativeProps);
}
renderLabel = (text, style) => {
return (
<Text style={style}>
{text}
</Text>
)
};
renderLeftElement = (element, style) => {
return (
<View style={StyleSheet.flatten([styles.leftElement, style])}>
{element}
</View>
)
};
renderRightElement = (element, style) => {
return (
<View style={StyleSheet.flatten([styles.rightElement, style])}>
{element}
</View>
)
};
// If characters remain in the input field after input is completed, add them to the tag.
onEndEditing = (tags, updateState) => {
if (tags.tag) {
const tempArray = tags.tagsArray.concat(tags.tag);
const tempObject = {
tag: '',
tagsArray: [...new Set(tempArray)] // Deduplication
};
updateState(tempObject);
return this.input.clear();
}
}
onChangeText = (text, tags, updateState, keysForTags, keysForTagsArray) => {
if (keysForTagsArray) {
return this.onChangeText2(text, tags, updateState, keysForTagsArray)
}
let keysStr;
if (typeof keysForTags === 'string') {
keysStr = keysForTags;
} else {
keysStr = ' ';
}
if (text.includes(keysStr)) {
if (text === keysStr) {
return
}
let tempTag = text.replace(keysStr, '');
const tempArray = tags.tagsArray.concat(tempTag);
let tempObject = {
tag: '',
tagsArray: [...new Set(tempArray)] // Deduplication
};
updateState(tempObject);
return this.input.clear();
}
let tempObject = {
tag: text,
tagsArray: tags.tagsArray
};
return updateState(tempObject)
};
onChangeText2 = (text, tags, updateState, keysForTagsArray) => {
// Escaping special characters.
const keys = keysForTagsArray.map((str) => (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"));
const regexp = new RegExp(keys.join('|'));
if (regexp.test(text)) {
if (keysForTagsArray.includes(text)) {
// The following processing is required because multiple characters may be specified as one delimiter.
let tempObject = {
tag: '',
tagsArray: tags.tagsArray,
};
updateState(tempObject);
return this.input.clear();
}
const tempTag = text.replace(regexp, '');
const tempArray = tags.tagsArray.concat(tempTag);
let tempObject = {
tag: '',
tagsArray: [...new Set(tempArray)] // Deduplication
};
updateState(tempObject);
return this.input.clear();
}
let tempObject = {
tag: text,
tagsArray: tags.tagsArray
};
return updateState(tempObject)
};
deleteTag = (tagToDelete, tags, updateState) => {
let tempArray = tags.tagsArray;
tempArray.splice(tagToDelete, 1);
let tempObject = {
tag: tags.tag,
tagsArray: tempArray
};
updateState(tempObject)
};
render() {
const {
containerStyle,
disabled,
disabledInputStyle,
inputContainerStyle,
leftElement,
leftElementContainerStyle,
rightElement,
rightElementContainerStyle,
inputStyle,
label,
labelStyle,
tags,
tagStyle,
tagTextStyle,
tagsViewStyle,
updateState,
keysForTag,
keysForTagsArray,
deleteElement,
deleteIconStyles,
customElement,
} = this.props;
const props = this.props;
return (
<View style={StyleSheet.flatten([styles.container, containerStyle])}>
{label ? this.renderLabel(label, StyleSheet.flatten([styles.labelStyle, labelStyle])) : null}
<View style={StyleSheet.flatten(StyleSheet.flatten([styles.inputContainer, inputContainerStyle]))}>
{leftElement ? this.renderLeftElement(leftElement, leftElementContainerStyle) : null}
<TextInput
underlineColorAndroid="transparent"
editable={!disabled}
ref={ref => {
this.input = ref;
}}
style={StyleSheet.flatten([
styles.input,
inputStyle,
disabled && styles.disabledInput,
disabled && disabledInputStyle,
])}
{...props}
value={tags.tag}
onChangeText={text => this.onChangeText(text, tags, updateState, keysForTag, keysForTagsArray)}
onEndEditing={() => this.onEndEditing(tags, updateState)}
/>
{rightElement ? this.renderRightElement(rightElement, rightElementContainerStyle) : null}
</View>
{customElement ? customElement : null}
<View style={StyleSheet.flatten([styles.tagsView, tagsViewStyle])}>
{tags.tagsArray.map((item, count) => {
return (
<View
style={StyleSheet.flatten([styles.tag, tagStyle])}
key={count}
>
<Text style={StyleSheet.flatten([styles.tagText, tagTextStyle])}>{item}</Text>
<TouchableOpacity onPressIn={() => this.deleteTag(count, tags, updateState) }>
{deleteElement ? deleteElement : (
<Image
source={require('./assets/close.png')}
style={StyleSheet.flatten([styles.deleteIcon, deleteIconStyles])}
/>
)}
</TouchableOpacity>
</View>
)
})}
</View>
</View>
);
}
}
Tags.propTypes = {
disabled: PropTypes.bool,
leftElement: PropTypes.element,
rightElement: PropTypes.element,
customElement: PropTypes.element,
label: PropTypes.string,
tags: PropTypes.object,
updateState: PropTypes.func,
keysForTag: PropTypes.string,
keysForTagsArray: PropTypes.arrayOf(PropTypes.string),
containerStyle: ViewPropTypes.style,
inputContainerStyle: ViewPropTypes.style,
inputStyle: TextInput.propTypes.style,
disabledInputStyle: ViewPropTypes.style,
leftElementContainerStyle: ViewPropTypes.style,
rightElementContainerStyle: ViewPropTypes.style,
labelStyle: Text.propTypes.style,
deleteIconStyles: ViewPropTypes.style,
};
const styles = {
container: {
width: '100%',
paddingHorizontal: 10,
},
disabledInput: {
opacity: 0.5,
},
inputContainer: {
flexDirection: 'row',
},
leftElement: {
height: 40,
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
},
rightElement: {
height: 40,
justifyContent: 'center',
alignItems: 'center',
marginRight: 10,
},
input: {
color: 'black',
fontSize: 18,
flex: 1,
minHeight: 40,
marginLeft: 5,
marginRight: 5,
},
tagsView: {
marginTop: 10,
flexDirection: 'row',
justifyContent: 'flex-start',
flexWrap: 'wrap',
},
tag: {
flexDirection: 'row',
height: 26,
borderRadius: 13,
backgroundColor: '#979797',
minWidth: 40,
maxWidth: 200,
justifyContent: 'space-between',
alignItems: 'center',
padding: 5,
margin: 5,
borderWidth: 0.5,
borderColor: 'gray'
},
tagText: {
marginHorizontal: 5
},
labelStyle: {
fontSize: 12,
marginTop: 12,
marginBottom: -4
},
deleteIcon: {
width: 20,
height: 20,
opacity: 0.5,
marginLeft: 5
}
};
export default Tags;