We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
If there is something that can be merged into the main branch.
source code:
import React, { Component } from 'react'; import { Text, View, Animated, TextInput, FlatList, } from 'react-native'; import PropTypes from 'prop-types'; export default class MentionsTextInput extends Component { constructor() { super(); this.state = { textInputHeight: "", isTrackingStarted: false, suggestionRowHeight: new Animated.Value(0), }; this.isTrackingStarted = false; this.previousChar = " "; } static getDerivedStateFromProps(nextProps, prevState) { if (!nextProps.value) { return { textInputHeight: nextProps.textInputMinHeight, isTrackingStarted: false, }; } else if (prevState.isTrackingStarted && !nextProps.horizontal && nextProps.suggestionsData.length !== 0) { const numOfRows = nextProps.MaxVisibleRowCount >= nextProps.suggestionsData.length ? nextProps.suggestionsData.length : nextProps.MaxVisibleRowCount; const height = numOfRows * nextProps.suggestionRowHeight; return { suggestionRowHeight: new Animated.Value(height), }; } return null; } componentDidMount() { this.setState({ textInputHeight: this.props.textInputMinHeight }); } startTracking() { this.isTrackingStarted = true; this.openSuggestionsPanel(); this.setState({ isTrackingStarted: true }); } stopTracking() { this.isTrackingStarted = false; this.closeSuggestionsPanel(); this.setState({ isTrackingStarted: false }); } openSuggestionsPanel(height) { Animated.timing(this.state.suggestionRowHeight, { toValue: height ? height : this.props.suggestionRowHeight, duration: 100, useNativeDriver: false, }).start(); } closeSuggestionsPanel() { Animated.timing(this.state.suggestionRowHeight, { toValue: 0, duration: 100, useNativeDriver: false, }).start(); } updateSuggestions(lastKeyword) { this.props.triggerCallback(lastKeyword); } identifyKeyword(val) { if (this.isTrackingStarted) { const boundary = this.props.triggerLocation === 'new-word-only' ? 'B' : ''; const pattern = new RegExp(`\\${boundary}${this.props.trigger}[a-z0-9_-]+|\\${boundary}${this.props.trigger}`, `gi`); const keywordArray = val.match(pattern); if (keywordArray && !!keywordArray.length) { const lastKeyword = keywordArray[keywordArray.length - 1]; this.updateSuggestions(lastKeyword); } } } onChangeText(val) { this.props.onChangeText(val); // pass changed text back const lastChar = val.substr(val.length - 1); const wordBoundry = (this.props.triggerLocation === 'new-word-only') ? this.previousChar.trim().length === 0 : true; if (lastChar === this.props.trigger && wordBoundry) { this.startTracking(); } else if (lastChar === ' ' && this.state.isTrackingStarted || val === "") { this.stopTracking(); } this.previousChar = lastChar; this.identifyKeyword(val); } resetTextbox() { this.previousChar = " "; this.stopTracking(); this.setState({ textInputHeight: this.props.textInputMinHeight }); } render() { return ( <View> <Animated.View style={[{ ...this.props.suggestionsPanelStyle }, { height: this.state.suggestionRowHeight }]}> <FlatList keyboardShouldPersistTaps={"always"} horizontal={this.props.horizontal} ListEmptyComponent={this.props.loadingComponent} enableEmptySections={true} data={this.props.suggestionsData} keyExtractor={this.props.keyExtractor} renderItem={(rowData) => { return this.props.renderSuggestionsRow(rowData, this.stopTracking.bind(this)) }} /> </Animated.View> <TextInput {...this.props} onContentSizeChange={(event) => { this.setState({ textInputHeight: this.props.textInputMinHeight >= event.nativeEvent.contentSize.height ? this.props.textInputMinHeight : event.nativeEvent.contentSize.height + 10, }); }} ref={component => this._textInput = component} onChangeText={this.onChangeText.bind(this)} multiline={true} value={this.props.value} style={[{ ...this.props.textInputStyle }, { height: Math.min(this.props.textInputMaxHeight, this.state.textInputHeight) }]} placeholder={this.props.placeholder ? this.props.placeholder : 'Write a comment...'} /> </View> ) } } MentionsTextInput.propTypes = { textInputStyle: PropTypes.object, suggestionsPanelStyle: PropTypes.object, loadingComponent: PropTypes.oneOfType([ PropTypes.func, PropTypes.element, ]), textInputMinHeight: PropTypes.number, textInputMaxHeight: PropTypes.number, trigger: PropTypes.string.isRequired, triggerLocation: PropTypes.oneOf(['new-word-only', 'anywhere']).isRequired, value: PropTypes.string.isRequired, onChangeText: PropTypes.func.isRequired, triggerCallback: PropTypes.func.isRequired, renderSuggestionsRow: PropTypes.oneOfType([ PropTypes.func, PropTypes.element, ]).isRequired, suggestionsData: PropTypes.array.isRequired, keyExtractor: PropTypes.func.isRequired, horizontal: PropTypes.bool, suggestionRowHeight: PropTypes.number.isRequired, MaxVisibleRowCount: function(props, propName, componentName) { if(!props.horizontal && !props.MaxVisibleRowCount) { return new Error( `Prop 'MaxVisibleRowCount' is required if horizontal is set to false.` ); } } }; MentionsTextInput.defaultProps = { textInputStyle: { borderColor: '#ebebeb', borderWidth: 1, fontSize: 15 }, suggestionsPanelStyle: { backgroundColor: 'rgba(100,100,100,0.1)' }, loadingComponent: () => <Text>Loading...</Text>, textInputMinHeight: 30, textInputMaxHeight: 80, horizontal: true, };
demo
import React, {useRef, useState} from "react"; import {TouchableOpacity, Text, View, ActivityIndicator, StyleSheet} from "react-native"; import tw from "../../utils/tailwind"; import MentionsTextInput from "./MentionsTextInput"; const DemoTags = ()=>{ const [value,setValue]=useState(''); const [data, setData]=useState([ {UserName:'UserName',DisplayName:'DisplayName'}, {UserName:'UserName1',DisplayName:'DisplayName1'}, {UserName:'UserName2',DisplayName:'DisplayName2'}, {UserName:'UserName3',DisplayName:'DisplayName3'}, ]); const [keyword,setKey]=useState(''); function triggerCallback(keyword){ setKey(keyword) } function onSuggestionTap(UserName, hidePanel){ hidePanel(); const comment = value.slice(0, - keyword.length) setValue(comment + '@' + UserName) } function renderSuggestionsRow({ item }, hidePanel){ return ( <TouchableOpacity onPress={() => onSuggestionTap(item.UserName, hidePanel)}> <View style={styles.suggestionsRowContainer}> <View style={styles.userIconBox}> <Text style={styles.usernameInitials}>{!!item.DisplayName && item.DisplayName.substring(0, 2).toUpperCase()}</Text> </View> <View style={styles.userDetailsBox}> <Text style={styles.displayNameText}>{item.DisplayName}</Text> <Text style={styles.usernameText}>@{item.UserName}</Text> </View> </View> </TouchableOpacity> ) } return ( <View style={tw`p-10`}> <MentionsTextInput textInputStyle={{ borderColor: '#ebebeb', borderWidth: 1, padding: 5, fontSize: 15 }} suggestionsPanelStyle={{ backgroundColor: 'rgba(100,100,100,0.1)' }} loadingComponent={() => <View style={{ flex: 1, width, justifyContent: 'center', alignItems: 'center' }}><ActivityIndicator /></View>} textInputMinHeight={30} textInputMaxHeight={80} trigger={'@'} triggerLocation={'new-word-only'} // 'new-word-only', 'anywhere' value={value} onChangeText={(val) => { setValue(val) }} triggerCallback={triggerCallback} renderSuggestionsRow={renderSuggestionsRow} suggestionsData={data} // array of objects keyExtractor={(item, index) => item.UserName} suggestionRowHeight={45} horizontal={false} // default is true, change the orientation of the list MaxVisibleRowCount={3} // this is required if horizontal={false} /> </View> ); } export default DemoTags const styles = StyleSheet.create({ container: { height: 300, justifyContent: 'flex-end', paddingTop: 100 }, suggestionsRowContainer: { flexDirection: 'row', }, userAvatarBox: { width: 35, paddingTop: 2 }, userIconBox: { height: 45, width: 45, alignItems: 'center', justifyContent: 'center', backgroundColor: '#54c19c' }, usernameInitials: { color: '#fff', fontWeight: '800', fontSize: 14 }, userDetailsBox: { flex: 1, justifyContent: 'center', paddingLeft: 10, paddingRight: 15 }, displayNameText: { fontSize: 13, fontWeight: '500' }, usernameText: { fontSize: 12, color: 'rgba(0,0,0,0.6)' } });
The text was updated successfully, but these errors were encountered:
No branches or pull requests
If there is something that can be merged into the main branch.
source code:
demo
The text was updated successfully, but these errors were encountered: