diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js index 6e2856a7e058..61f6981edbbe 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.js +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js @@ -65,7 +65,6 @@ class EmojiPickerMenu extends Component { this.filterEmojis = _.debounce(this.filterEmojis.bind(this), 300); this.highlightAdjacentEmoji = this.highlightAdjacentEmoji.bind(this); - this.scrollToHighlightedIndex = this.scrollToHighlightedIndex.bind(this); this.setupEventHandlers = this.setupEventHandlers.bind(this); this.cleanupEventHandlers = this.cleanupEventHandlers.bind(this); this.renderItem = this.renderItem.bind(this); @@ -76,7 +75,6 @@ class EmojiPickerMenu extends Component { this.getItemLayout = this.getItemLayout.bind(this); this.scrollToHeader = this.scrollToHeader.bind(this); - this.currentScrollOffset = 0; this.firstNonHeaderIndex = 0; const {filteredEmojis, headerEmojis, headerRowIndices} = this.getEmojisAndHeaderRowIndices(); @@ -299,9 +297,9 @@ class EmojiPickerMenu extends Component { return; } - // Blur the input and change the highlight type to keyboard + // Blur the input, change the highlight type to keyboard, and disable pointer events this.searchInput.blur(); - this.setState({isUsingKeyboardMovement: true}); + this.setState({isUsingKeyboardMovement: true, arePointerEventsDisabled: true}); // We only want to hightlight the Emoji if none was highlighted already // If we already have a highlighted Emoji, lets just skip the first navigation @@ -311,10 +309,9 @@ class EmojiPickerMenu extends Component { } // If nothing is highlighted and an arrow key is pressed - // select the first emoji + // select the first emoji, apply keyboard movement styles, and disable pointer events if (this.state.highlightedIndex === -1) { - this.setState({highlightedIndex: this.firstNonHeaderIndex}); - this.scrollToHighlightedIndex(); + this.setState({highlightedIndex: this.firstNonHeaderIndex, isUsingKeyboardMovement: true, arePointerEventsDisabled: true}); return; } @@ -368,10 +365,9 @@ class EmojiPickerMenu extends Component { break; } - // Actually highlight the new emoji, apply keyboard movement styles, and scroll to it if the index was changed + // Actually highlight the new emoji, apply keyboard movement styles, and disable pointer events if (newIndex !== this.state.highlightedIndex) { - this.setState({highlightedIndex: newIndex, isUsingKeyboardMovement: true}); - this.scrollToHighlightedIndex(); + this.setState({highlightedIndex: newIndex, isUsingKeyboardMovement: true, arePointerEventsDisabled: true}); } } @@ -381,36 +377,6 @@ class EmojiPickerMenu extends Component { this.emojiList.scrollToOffset({offset: calculatedOffset, animated: true}); } - /** - * Calculates the required scroll offset (aka distance from top) and scrolls the FlatList to the highlighted emoji - * if any portion of it falls outside of the window. - * Doing this because scrollToIndex doesn't work as expected. - */ - scrollToHighlightedIndex() { - // Calculate the number of rows above the current row, then add 1 to include the current row - const numRows = Math.floor(this.state.highlightedIndex / CONST.EMOJI_NUM_PER_ROW) + 1; - - // The scroll offsets at the top and bottom of the highlighted emoji - const offsetAtEmojiBottom = numRows * CONST.EMOJI_PICKER_HEADER_HEIGHT; - const offsetAtEmojiTop = offsetAtEmojiBottom - CONST.EMOJI_PICKER_ITEM_HEIGHT; - - // Scroll to fit the entire highlighted emoji into the window if we need to - let targetOffset = this.currentScrollOffset; - if (offsetAtEmojiBottom - this.currentScrollOffset >= CONST.NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT) { - targetOffset = offsetAtEmojiBottom - CONST.NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT; - } else if (offsetAtEmojiTop - CONST.EMOJI_PICKER_HEADER_HEIGHT <= this.currentScrollOffset) { - // There is always a sticky header on the top, subtract the EMOJI_PICKER_HEADER_HEIGHT from offsetAtEmojiTop to get the correct scroll position. - targetOffset = offsetAtEmojiTop - CONST.EMOJI_PICKER_HEADER_HEIGHT; - } - if (targetOffset !== this.currentScrollOffset) { - // Disable pointer events so that onHover doesn't get triggered when the items move while we're scrolling - if (!this.state.arePointerEventsDisabled) { - this.setState({arePointerEventsDisabled: true}); - } - this.emojiList.scrollToOffset({offset: targetOffset, animated: false}); - } - } - /** * Filter the entire list of emojis to only emojis that have the search term in their keywords * @@ -530,6 +496,7 @@ class EmojiPickerMenu extends Component { return ( @@ -566,10 +533,11 @@ class EmojiPickerMenu extends Component { {overscrollBehaviorY: 'contain'}, // Set overflow to hidden to prevent elastic scrolling when there are not enough contents to scroll in FlatList {overflowY: this.state.filteredEmojis.length > overflowLimit ? 'auto' : 'hidden'}, + // Set scrollPaddingTop to consider sticky headers while scrolling + {scrollPaddingTop: isFiltered ? 0 : CONST.EMOJI_PICKER_ITEM_HEIGHT}, ]} extraData={[this.state.filteredEmojis, this.state.highlightedIndex, this.props.preferredSkinTone]} stickyHeaderIndices={this.state.headerIndices} - onScroll={(e) => (this.currentScrollOffset = e.nativeEvent.contentOffset.y)} getItemLayout={this.getItemLayout} contentContainerStyle={styles.flexGrow1} ListEmptyComponent={{this.props.translate('common.noResultsFound')}} diff --git a/src/components/EmojiPicker/EmojiPickerMenuItem/index.js b/src/components/EmojiPicker/EmojiPickerMenuItem/index.js index 37e90f01c707..728e56792ddb 100644 --- a/src/components/EmojiPicker/EmojiPickerMenuItem/index.js +++ b/src/components/EmojiPicker/EmojiPickerMenuItem/index.js @@ -42,13 +42,14 @@ class EmojiPickerMenuItem extends PureComponent { super(props); this.ref = null; + this.focusAndScroll = this.focusAndScroll.bind(this); } componentDidMount() { if (!this.props.isFocused) { return; } - this.ref.focus(); + this.focusAndScroll(); } componentDidUpdate(prevProps) { @@ -58,7 +59,12 @@ class EmojiPickerMenuItem extends PureComponent { if (!this.props.isFocused) { return; } - this.ref.focus(); + this.focusAndScroll(); + } + + focusAndScroll() { + this.ref.focus({preventScroll: true}); + this.ref.scrollIntoView({block: 'nearest'}); } render() {