Skip to content

Commit

Permalink
Merge pull request #26633 from DrLoopFall/fix-scrolling-emoji-picker-…
Browse files Browse the repository at this point in the history
…with-arrows-24471

Fix scrolling emoji picker with arrows
  • Loading branch information
jasperhuangg authored Sep 5, 2023
2 parents cbda2b0 + bcf7389 commit d447b95
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 43 deletions.
50 changes: 9 additions & 41 deletions src/components/EmojiPicker/EmojiPickerMenu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand All @@ -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;
}

Expand Down Expand Up @@ -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});
}
}

Expand All @@ -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
*
Expand Down Expand Up @@ -530,6 +496,7 @@ class EmojiPickerMenu extends Component {
return (
<View
style={[styles.emojiPickerContainer, StyleUtils.getEmojiPickerStyle(this.props.isSmallScreenWidth)]}
// Disable pointer events so that onHover doesn't get triggered when the items move while we're scrolling
pointerEvents={this.state.arePointerEventsDisabled ? 'none' : 'auto'}
>
<View style={[styles.ph4, styles.pb3, styles.pt2]}>
Expand Down Expand Up @@ -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={<Text style={[styles.textLabel, styles.colorMuted]}>{this.props.translate('common.noResultsFound')}</Text>}
Expand Down
10 changes: 8 additions & 2 deletions src/components/EmojiPicker/EmojiPickerMenuItem/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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() {
Expand Down

0 comments on commit d447b95

Please sign in to comment.