MM-12189 Set autocomplete max height based off available space (#2218)

* MM-12189 Set autocomplete max height based off available space

* Have each autocomplete type determine its own max height since DateSuggestion needs more space

* Remove unused props and ref
This commit is contained in:
Harrison Healey 2018-10-05 09:58:05 -04:00 committed by GitHub
parent dd506d4c9f
commit 4e251d392a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 85 additions and 61 deletions

View file

@ -26,8 +26,8 @@ export default class AtMention extends PureComponent {
defaultChannel: PropTypes.object,
inChannel: PropTypes.array,
isSearch: PropTypes.bool,
listHeight: PropTypes.number,
matchTerm: PropTypes.string,
maxListHeight: PropTypes.number,
onChangeText: PropTypes.func.isRequired,
onResultCountChange: PropTypes.func.isRequired,
outChannel: PropTypes.array,
@ -204,7 +204,7 @@ export default class AtMention extends PureComponent {
};
render() {
const {isSearch, listHeight, theme} = this.props;
const {maxListHeight, theme} = this.props;
const {mentionComplete, sections} = this.state;
if (sections.length === 0 || mentionComplete) {
@ -219,7 +219,7 @@ export default class AtMention extends PureComponent {
<SectionList
keyboardShouldPersistTaps='always'
keyExtractor={this.keyExtractor}
style={[style.listView, isSearch ? [style.search, {height: listHeight}] : null]}
style={[style.listView, {maxHeight: maxListHeight}]}
sections={sections}
renderItem={this.renderItem}
renderSectionHeader={this.renderSectionHeader}
@ -235,8 +235,5 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
listView: {
backgroundColor: theme.centerChannelBg,
},
search: {
minHeight: 125,
},
};
});

View file

@ -23,6 +23,7 @@ export default class Autocomplete extends PureComponent {
cursorPosition: PropTypes.number.isRequired,
deviceHeight: PropTypes.number,
onChangeText: PropTypes.func.isRequired,
maxHeight: PropTypes.number,
rootId: PropTypes.string,
isSearch: PropTypes.bool,
theme: PropTypes.object.isRequired,
@ -84,12 +85,21 @@ export default class Autocomplete extends PureComponent {
this.setState({keyboardOffset: 0});
};
listHeight() {
let offset = Platform.select({ios: 65, android: 75});
if (DeviceInfo.getModel().includes('iPhone X')) {
offset = 90;
maxListHeight() {
let maxHeight;
if (this.props.maxHeight) {
maxHeight = this.props.maxHeight;
} else {
// List is expanding downwards, likely from the search box
let offset = Platform.select({ios: 65, android: 75});
if (DeviceInfo.getModel().includes('iPhone X')) {
offset = 90;
}
maxHeight = this.props.deviceHeight - offset - this.state.keyboardOffset;
}
return this.props.deviceHeight - offset - this.state.keyboardOffset;
return maxHeight;
}
render() {
@ -113,26 +123,29 @@ export default class Autocomplete extends PureComponent {
containerStyle.push(style.borders);
}
}
const listHeight = this.listHeight();
const maxListHeight = this.maxListHeight();
return (
<View style={wrapperStyle}>
<View style={containerStyle}>
<AtMention
listHeight={listHeight}
maxListHeight={maxListHeight}
onResultCountChange={this.handleAtMentionCountChange}
{...this.props}
/>
<ChannelMention
listHeight={listHeight}
maxListHeight={maxListHeight}
onResultCountChange={this.handleChannelMentionCountChange}
{...this.props}
/>
<EmojiSuggestion
maxListHeight={maxListHeight}
onResultCountChange={this.handleEmojiCountChange}
{...this.props}
/>
<SlashSuggestion
maxListHeight={maxListHeight}
onResultCountChange={this.handleCommandCountChange}
{...this.props}
/>
@ -167,7 +180,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
},
container: {
bottom: 0,
maxHeight: 200,
},
content: {
flex: 1,

View file

@ -25,8 +25,8 @@ export default class ChannelMention extends PureComponent {
currentTeamId: PropTypes.string.isRequired,
cursorPosition: PropTypes.number.isRequired,
isSearch: PropTypes.bool,
listHeight: PropTypes.number,
matchTerm: PropTypes.string,
maxListHeight: PropTypes.number,
myChannels: PropTypes.array,
myMembers: PropTypes.object,
otherChannels: PropTypes.array,
@ -194,7 +194,7 @@ export default class ChannelMention extends PureComponent {
};
render() {
const {isSearch, listHeight, theme} = this.props;
const {maxListHeight, theme} = this.props;
const {mentionComplete, sections} = this.state;
if (sections.length === 0 || mentionComplete) {
@ -209,7 +209,7 @@ export default class ChannelMention extends PureComponent {
<SectionList
keyboardShouldPersistTaps='always'
keyExtractor={this.keyExtractor}
style={[style.listView, isSearch ? [style.search, {height: listHeight}] : null]}
style={[style.listView, {maxHeight: maxListHeight}]}
sections={sections}
renderItem={this.renderItem}
renderSectionHeader={this.renderSectionHeader}
@ -225,8 +225,5 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
listView: {
backgroundColor: theme.centerChannelBg,
},
search: {
minHeight: 125,
},
};
});

View file

@ -15,7 +15,6 @@ import {changeOpacity} from 'app/utils/theme';
export default class DateSuggestion extends PureComponent {
static propTypes = {
cursorPosition: PropTypes.number.isRequired,
listHeight: PropTypes.number,
locale: PropTypes.string.isRequired,
matchTerm: PropTypes.string,
onChangeText: PropTypes.func.isRequired,

View file

@ -29,6 +29,7 @@ export default class EmojiSuggestion extends Component {
emojis: PropTypes.array.isRequired,
isSearch: PropTypes.bool,
fuse: PropTypes.object.isRequired,
maxListHeight: PropTypes.number,
theme: PropTypes.object.isRequired,
onChangeText: PropTypes.func.isRequired,
onResultCountChange: PropTypes.func.isRequired,
@ -171,18 +172,20 @@ export default class EmojiSuggestion extends Component {
getItemLayout = ({index}) => ({length: 40, offset: 40 * index, index})
render() {
const {maxListHeight, theme} = this.props;
if (!this.state.active) {
// If we are not in an active state return null so nothing is rendered
// other components are not blocked.
return null;
}
const style = getStyleFromTheme(this.props.theme);
const style = getStyleFromTheme(theme);
return (
<FlatList
keyboardShouldPersistTaps='always'
style={style.listView}
style={[style.listView, {maxHeight: maxListHeight}]}
extraData={this.state}
data={this.state.dataSource}
keyExtractor={this.keyExtractor}

View file

@ -25,6 +25,7 @@ export default class SlashSuggestion extends Component {
commands: PropTypes.array,
commandsRequest: PropTypes.object.isRequired,
isSearch: PropTypes.bool,
maxListHeight: PropTypes.number,
theme: PropTypes.object.isRequired,
onChangeText: PropTypes.func.isRequired,
onResultCountChange: PropTypes.func.isRequired,
@ -133,18 +134,20 @@ export default class SlashSuggestion extends Component {
)
render() {
const {maxListHeight, theme} = this.props;
if (!this.state.active) {
// If we are not in an active state return null so nothing is rendered
// other components are not blocked.
return null;
}
const style = getStyleFromTheme(this.props.theme);
const style = getStyleFromTheme(theme);
return (
<FlatList
keyboardShouldPersistTaps='always'
style={style.listView}
style={[style.listView, {maxHeight: maxListHeight}]}
extraData={this.state}
data={this.state.dataSource}
keyExtractor={this.keyExtractor}

View file

@ -17,11 +17,9 @@ export default class FileUploadPreview extends PureComponent {
static propTypes = {
channelId: PropTypes.string.isRequired,
channelIsLoading: PropTypes.bool,
createPostRequestStatus: PropTypes.string.isRequired,
deviceHeight: PropTypes.number.isRequired,
files: PropTypes.array.isRequired,
filesUploadingForCurrentChannel: PropTypes.bool.isRequired,
inputHeight: PropTypes.number.isRequired,
rootId: PropTypes.string,
showFileMaxWarning: PropTypes.bool.isRequired,
theme: PropTypes.object.isRequired,

View file

@ -14,7 +14,6 @@ function mapStateToProps(state, ownProps) {
return {
channelIsLoading: state.views.channel.loading,
createPostRequestStatus: state.requests.posts.createPost.status,
deviceHeight,
filesUploadingForCurrentChannel: checkForFileUploadingInChannel(state, ownProps.channelId, ownProps.rootId),
theme: getTheme(state),

View file

@ -21,6 +21,9 @@ import FormattedText from 'app/components/formatted_text';
import Typing from './components/typing';
const AUTOCOMPLETE_MARGIN = 20;
const AUTOCOMPLETE_MAX_HEIGHT = 200;
let PaperPlane = null;
export default class PostTextbox extends PureComponent {
@ -76,6 +79,7 @@ export default class PostTextbox extends PureComponent {
contentHeight: INITIAL_HEIGHT,
cursorPosition: 0,
keyboardType: 'default',
top: 0,
value: props.value,
showFileMaxWarning: false,
};
@ -105,10 +109,6 @@ export default class PostTextbox extends PureComponent {
}
}
attachAutocomplete = (c) => {
this.autocomplete = c;
};
blur = () => {
if (this.refs.input) {
this.refs.input.blur();
@ -487,6 +487,12 @@ export default class PostTextbox extends PureComponent {
</View>);
};
handleLayout = (e) => {
this.setState({
top: e.nativeEvent.layout.y,
});
};
render() {
const {intl} = this.context;
const {
@ -514,7 +520,13 @@ export default class PostTextbox extends PureComponent {
);
}
const {contentHeight, cursorPosition, showFileMaxWarning, value} = this.state;
const {
contentHeight,
cursorPosition,
showFileMaxWarning,
top,
value,
} = this.state;
const textInputHeight = Math.min(contentHeight, MAX_CONTENT_HEIGHT);
const textValue = channelIsLoading ? '' : value;
@ -547,48 +559,52 @@ export default class PostTextbox extends PureComponent {
}
return (
<View>
<React.Fragment>
<Typing/>
<FileUploadPreview
channelId={channelId}
files={files}
inputHeight={textInputHeight}
rootId={rootId}
showFileMaxWarning={showFileMaxWarning}
/>
<Autocomplete
ref={this.attachAutocomplete}
cursorPosition={cursorPosition}
maxHeight={Math.min(top - AUTOCOMPLETE_MARGIN, AUTOCOMPLETE_MAX_HEIGHT)}
onChangeText={this.handleTextChange}
value={this.state.value}
rootId={rootId}
/>
{!channelIsArchived && <View style={style.inputWrapper}>
{!channelIsReadOnly && attachmentButton}
<View style={[inputContainerStyle, (channelIsReadOnly && {marginLeft: 10})]}>
<TextInput
ref='input'
value={textValue}
onChangeText={this.handleTextChange}
onSelectionChange={this.handlePostDraftSelectionChanged}
placeholder={intl.formatMessage(placeholder)}
placeholderTextColor={changeOpacity('#000', 0.5)}
multiline={true}
numberOfLines={5}
blurOnSubmit={false}
underlineColorAndroid='transparent'
style={[style.input, Platform.OS === 'android' ? {height: textInputHeight} : {maxHeight: MAX_CONTENT_HEIGHT}]}
onContentSizeChange={this.handleContentSizeChange}
keyboardType={this.state.keyboardType}
onEndEditing={this.handleEndEditing}
disableFullscreenUI={true}
editable={!channelIsReadOnly}
/>
{this.renderSendButton()}
{!channelIsArchived && (
<View
style={style.inputWrapper}
onLayout={this.handleLayout}
>
{!channelIsReadOnly && attachmentButton}
<View style={[inputContainerStyle, (channelIsReadOnly && {marginLeft: 10})]}>
<TextInput
ref='input'
value={textValue}
onChangeText={this.handleTextChange}
onSelectionChange={this.handlePostDraftSelectionChanged}
placeholder={intl.formatMessage(placeholder)}
placeholderTextColor={changeOpacity('#000', 0.5)}
multiline={true}
numberOfLines={5}
blurOnSubmit={false}
underlineColorAndroid='transparent'
style={[style.input, Platform.OS === 'android' ? {height: textInputHeight} : {maxHeight: MAX_CONTENT_HEIGHT}]}
onContentSizeChange={this.handleContentSizeChange}
keyboardType={this.state.keyboardType}
onEndEditing={this.handleEndEditing}
disableFullscreenUI={true}
editable={!channelIsReadOnly}
/>
{this.renderSendButton()}
</View>
</View>
</View>}
)}
{channelIsArchived && this.archivedView(theme, style)}
</View>
</React.Fragment>
);
}
}