Skip to content

Instantly share code, notes, and snippets.

@tioback
Created September 26, 2016 19:42
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tioback/6af21db0685cd3b1de28b84981f31cca to your computer and use it in GitHub Desktop.
Save tioback/6af21db0685cd3b1de28b84981f31cca to your computer and use it in GitHub Desktop.
Workaround for using zIndex with Android.
import React, {Component} from "react";
import {
Dimensions,
Image,
Keyboard,
Modal,
Platform,
ScrollView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View
} from "react-native";
class InputWithSuggestions extends Component {
constructor(props) {
super(props);
this._renderSuggestions = this._renderSuggestions.bind(this);
this._selectItem = this._selectItem.bind(this);
let { value = "", options = [], zIndex = 1 } = props;
let optionsMap = options.reduce((r, c) => ({...r, [c.id]: c}), {});
this.state = {
optionsMap,
query: props.renderLabel(value) || "",
isFiltering: false,
filteredData: [],
suggestionsLayout: {},
zIndex
}
}
_renderSuggestions() {
let { zIndex, query, suggestionsLayout, filteredData, isFiltering } = this.state;
let { renderLabel, renderKey } = this.props;
if (Platform.OS === "ios" && !isFiltering) {
return null;
}
let filterContent = () => {
if (filteredData.length === 0) {
return <Text style={styles.suggestionText}>No results found</Text>;
}
return (
// Se adicionar no ScrollView, aparece a borda estranha
// height: Math.max(150, this.state.suggestionsLayout.height)
// flex: 1 no contentContainerStyle não muda nada
<ScrollView
// style={{zIndex: zIndex + 2}}
>
<View onLayout={({nativeEvent}) => this.setState({suggestionsLayout: nativeEvent.layout})}>
{filteredData.map(option => (
<TouchableOpacity
key={renderKey(option)}
style={styles.suggestion}
onPress={() => this._selectItem(option)}
>
<Text style={styles.suggestionText}>{this.underline(renderLabel(option))}</Text>
</TouchableOpacity>
))}
</View>
</ScrollView>
);
}
let {content, conditionalLayout} = (!isFiltering || query.length < 2) ? ({
conditionalLayout: {},
content: null
}) : ({
conditionalLayout: {
zIndex: zIndex + 1,
borderWidth: .5,
elevation: zIndex + 1,
height: Math.min(150, suggestionsLayout.height)
},
content: filterContent()
});
return (
<View
style={[styles.suggestionsContainer, conditionalLayout]}
>
{content}
</View>
);
}
underline(name) {
let { query } = this.state;
let index = name.toLowerCase().indexOf(query.toLowerCase());
return (
<Text numberOfLines={1}>
{name.substring(0, index)}
<Text style={styles.underline}>{name.substring(index, index + query.length)}</Text>
{name.substring(index + query.length)}
</Text>
);
}
_onChangeText(text) {
let { renderLabel, options } = this.props;
this.setState({
isFiltering: true,
query: text,
filteredData: options
.sort((c1, c2) => renderLabel(c1).localeCompare(renderLabel(c2)))
.filter(c => text !== "" && renderLabel(c).toLowerCase().indexOf(text.toLowerCase()) > -1 )
});
}
_onSubmitEditing() {
this._selectItem(this.state.filteredData[0]);
}
_selectItem(item) {
this.setState({
zIndex: this.props.zIndex || 1,
isFiltering: false,
query: item ? this.props.renderLabel(item) : ""
})
if (item) {
this.props.onSelectOption(item.id);
}
}
render() {
let { query = "", zIndex, containerLayout } = this.state;
let { placeholder, scrollRef, top, onFocus } = this.props;
return (
<View
style={Platform.OS === "ios" ? {zIndex: zIndex + (query.length > 0 ? 1 : 0)} : {}}
ref="container"
>
<View style={query.length > 0 ? styles.selectedInputContainer : styles.inputContainer}>
<TextInput
autoCapitalize={"none"}
returnKeyType="done"
autoCapitalize="words"
autoCorrect={false}
placeholder={placeholder}
placeholderTextColor="#8F2692"
style={styles.input}
underlineColorAndroid="transparent"
onChangeText={this._onChangeText.bind(this)}
value={query}
onFocus={({nativeEvent}) => {
onFocus();
this.setState({zIndex: zIndex + 2})
if (scrollRef) {
scrollRef.scrollTo({y: top - (Platform.OS === "ios" ? 10 : 15)})
}
}}
onSubmitEditing={this._onSubmitEditing.bind(this)}
/>
</View>
{this._renderSuggestions()}
</View>
);
}
}
InputWithSuggestions.defaultProps = {
options: [],
renderLabel: option => option,
renderKey: option => option,
onFocus: () => {}
}
const styles = StyleSheet.create({
input: {
height: 22,
backgroundColor: "transparent",
padding: 0,
fontFamily: "Roboto-Thin",
fontSize: 20
},
inputContainer: {
borderWidth: .5,
borderRadius: 5,
borderColor: "#8F2692",
padding: 4,
height: 30,
justifyContent: "center",
},
selectedInputContainer: {
borderWidth: 1.5,
borderColor: "black",
borderRadius: 5,
padding: 4,
height: 30,
justifyContent: "center",
elevation: 2
},
underline: {
textDecorationStyle: "solid",
textDecorationLine: "underline"
},
suggestionsContainer: {
backgroundColor: "white",
position: "absolute",
right: 4,
left: 4,
borderColor: "#8F2692",
borderWidth: 0,
elevation: 1,
borderBottomLeftRadius: 5,
borderBottomRightRadius: 5
},
suggestion: {
padding: 8,
paddingVertical: 14
},
suggestionText: {
}
});
import InputWithSuggestions from "./InputWithSuggestions";
const options = ["Carlos Bar", "Carlitos Bar", "João Bar", "José Bar", "Maria Bar", "Bruxa Bar", "Bruna Bar", "Carlos Foo", "Carlitos Foo", "João Foo", "José Foo", "Maria Foo", "Bruxa Foo", "Bruna Foo"].map((n, i) => ({name: n, id: i+"_"+n}));
const size = 25;
const noop = () => {};
class Sandbox extends React.Component {
constructor() {
super();
this.state = {
visibleHeight: Dimensions.get("window").height
}
}
render() {
return (
<ScrollView
style={{height: this.state.visibleHeight, marginTop: 20}}
contentContainerStyle={{flex: 1}}
>
<Text style={{paddingTop: 20}}>Sandbox</Text>
<View style={{flex: 1, borderWidth: 1}}>
<InputWithSuggestions
placeholder="Choose a specialty"
options={options}
value={options[3]}
onSelectOption={option => console.log("specialty selected", option)}
renderLabel={option => option.name}
renderKey={option => option.id}
/>
<View style={{height: 20}} />
<InputWithSuggestions
placeholder="Choose a specialty"
options={options}
value={options[5]}
onSelectOption={option => console.log("specialty selected", option)}
renderLabel={option => option.name}
renderKey={option => option.id}
/>
<View style={{flex: 1}} />
</View>
</ScrollView>
);
}
}
@Msvargas97
Copy link

Thanks =) !!

@milosa
Copy link

milosa commented Sep 6, 2019

I'm making an autocomplete component of my own. I'm using your version to figure out how to get the suggestionlist to overlap.

Anyway, I made a runnable copy of this example on expo:
https://snack.expo.io/HJ5yVylUH

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment