Skip to content

Commit

Permalink
Merge pull request #45076 from margelo/perunt/suggestion-box-scroll-hide
Browse files Browse the repository at this point in the history
Hide suggestion box
  • Loading branch information
lakchote authored Jul 18, 2024
2 parents 8d80ccc + 813b8a4 commit c951669
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, {useCallback} from 'react';
import {View} from 'react-native';
import type {PointerEvent} from 'react-native';
import type PressableProps from '@components/Pressable/GenericPressable/types';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';

type TransparentOverlayProps = {
resetSuggestions: () => void;
};

type OnPressHandler = PressableProps['onPress'];

function TransparentOverlay({resetSuggestions}: TransparentOverlayProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();

const onResetSuggestions = useCallback<NonNullable<OnPressHandler>>(
(event) => {
event?.preventDefault();
resetSuggestions();
},
[resetSuggestions],
);

const handlePointerDown = useCallback((e: PointerEvent) => {
e?.preventDefault();
}, []);

return (
<View
onPointerDown={handlePointerDown}
style={styles.fullScreen}
>
<PressableWithoutFeedback
onPress={onResetSuggestions}
style={[styles.flex1, styles.cursorDefault]}
accessibilityLabel={translate('common.close')}
role={CONST.ROLE.BUTTON}
/>
</View>
);
}

export default TransparentOverlay;
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import {View} from 'react-native';
import BaseAutoCompleteSuggestions from '@components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions';
import useStyleUtils from '@hooks/useStyleUtils';
import getBottomSuggestionPadding from './getBottomSuggestionPadding';
import TransparentOverlay from './TransparentOverlay/TransparentOverlay';
import type {AutoCompleteSuggestionsPortalProps} from './types';

function AutoCompleteSuggestionsPortal<TSuggestion>({left = 0, width = 0, bottom = 0, ...props}: AutoCompleteSuggestionsPortalProps<TSuggestion>) {
function AutoCompleteSuggestionsPortal<TSuggestion>({left = 0, width = 0, bottom = 0, resetSuggestions = () => {}, ...props}: AutoCompleteSuggestionsPortalProps<TSuggestion>) {
const StyleUtils = useStyleUtils();
const styles = useMemo(() => StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom + getBottomSuggestionPadding()}), [StyleUtils, left, width, bottom]);

Expand All @@ -16,6 +17,7 @@ function AutoCompleteSuggestionsPortal<TSuggestion>({left = 0, width = 0, bottom

return (
<Portal hostName="suggestions">
<TransparentOverlay resetSuggestions={resetSuggestions} />
<View style={styles}>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<BaseAutoCompleteSuggestions<TSuggestion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {View} from 'react-native';
import BaseAutoCompleteSuggestions from '@components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions';
import useStyleUtils from '@hooks/useStyleUtils';
import getBottomSuggestionPadding from './getBottomSuggestionPadding';
import TransparentOverlay from './TransparentOverlay/TransparentOverlay';
import type {AutoCompleteSuggestionsPortalProps} from './types';

/**
Expand All @@ -14,7 +15,13 @@ import type {AutoCompleteSuggestionsPortalProps} from './types';
* On the native platform, tapping on auto-complete suggestions will not blur the main input.
*/

function AutoCompleteSuggestionsPortal<TSuggestion>({left = 0, width = 0, bottom = 0, ...props}: AutoCompleteSuggestionsPortalProps<TSuggestion>): ReactElement | null | false {
function AutoCompleteSuggestionsPortal<TSuggestion>({
left = 0,
width = 0,
bottom = 0,
resetSuggestions = () => {},
...props
}: AutoCompleteSuggestionsPortalProps<TSuggestion>): ReactElement | null | false {
const StyleUtils = useStyleUtils();

const bodyElement = document.querySelector('body');
Expand All @@ -31,7 +38,10 @@ function AutoCompleteSuggestionsPortal<TSuggestion>({left = 0, width = 0, bottom
!!width &&
bodyElement &&
ReactDOM.createPortal(
<View style={StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom - getBottomSuggestionPadding()})}>{componentToRender}</View>,
<>
<TransparentOverlay resetSuggestions={resetSuggestions} />
<View style={StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom - getBottomSuggestionPadding()})}>{componentToRender}</View>
</>,
bodyElement,
)
);
Expand Down
19 changes: 13 additions & 6 deletions src/components/AutoCompleteSuggestions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ function isEnoughSpaceToRenderMenuAboveCursor({y, cursorCoordinates, scrollValue
return y + (cursorCoordinates.y - scrollValue) > contentHeight + topInset + CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE;
}

const initialContainerState = {
width: 0,
left: 0,
bottom: 0,
cursorCoordinates: {x: 0, y: 0},
};

/**
* On the mobile-web platform, when long-pressing on auto-complete suggestions,
* we need to prevent focus shifting to avoid blurring the main input (which makes the suggestions picker close and fires the onSelect callback).
Expand All @@ -48,12 +55,7 @@ function AutoCompleteSuggestions<TSuggestion>({measureParentContainerAndReportCu
const prevLeftValue = React.useRef<number>(0);
const {windowHeight, windowWidth, isSmallScreenWidth} = useWindowDimensions();
const [suggestionHeight, setSuggestionHeight] = React.useState(0);
const [containerState, setContainerState] = React.useState({
width: 0,
left: 0,
bottom: 0,
cursorCoordinates: {x: 0, y: 0},
});
const [containerState, setContainerState] = React.useState(initialContainerState);
const StyleUtils = useStyleUtils();
const insets = useSafeAreaInsets();
const {keyboardHeight} = useKeyboardState();
Expand All @@ -80,6 +82,11 @@ function AutoCompleteSuggestions<TSuggestion>({measureParentContainerAndReportCu
return;
}

if (!windowHeight || !windowWidth || !suggestionsLength) {
setContainerState(initialContainerState);
return;
}

measureParentContainerAndReportCursor(({x, y, width, scrollValue, cursorCoordinates}: MeasureParentContainerAndCursor) => {
const xCoordinatesOfCursor = x + cursorCoordinates.x;
const bigScreenLeftOffset =
Expand Down
3 changes: 3 additions & 0 deletions src/components/AutoCompleteSuggestions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ type AutoCompleteSuggestionsProps<TSuggestion> = {

/** Measures the parent container's position and dimensions. Also add a cursor coordinates */
measureParentContainerAndReportCursor?: (props: MeasureParentContainerAndCursorCallback) => void;

/** Reset the emoji suggestions */
resetSuggestions?: () => void;
};

export type {AutoCompleteSuggestionsProps, RenderSuggestionMenuItemProps, MeasureParentContainerAndCursorCallback, MeasureParentContainerAndCursor};
5 changes: 5 additions & 0 deletions src/components/EmojiSuggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type EmojiSuggestionsProps = {

/** Measures the parent container's position and dimensions. Also add cursor coordinates */
measureParentContainerAndReportCursor: (callback: MeasureParentContainerAndCursorCallback) => void;

/** Reset the emoji suggestions */
resetSuggestions: () => void;
};

/**
Expand All @@ -49,6 +52,7 @@ function EmojiSuggestions({
preferredSkinToneIndex,
highlightedEmojiIndex = 0,
measureParentContainerAndReportCursor = () => {},
resetSuggestions,
}: EmojiSuggestionsProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
Expand Down Expand Up @@ -93,6 +97,7 @@ function EmojiSuggestions({
isSuggestionPickerLarge={isEmojiPickerLarge}
accessibilityLabelExtractor={keyExtractor}
measureParentContainerAndReportCursor={measureParentContainerAndReportCursor}
resetSuggestions={resetSuggestions}
/>
);
}
Expand Down
14 changes: 13 additions & 1 deletion src/components/MentionSuggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,25 @@ type MentionSuggestionsProps = {

/** Measures the parent container's position and dimensions. Also add cursor coordinates */
measureParentContainerAndReportCursor: (callback: MeasureParentContainerAndCursorCallback) => void;

/** Reset the emoji suggestions */
resetSuggestions: () => void;
};

/**
* Create unique keys for each mention item
*/
const keyExtractor = (item: Mention) => item.alternateText;

function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSelect, isMentionPickerLarge, measureParentContainerAndReportCursor = () => {}}: MentionSuggestionsProps) {
function MentionSuggestions({
prefix,
mentions,
highlightedMentionIndex = 0,
onSelect,
isMentionPickerLarge,
measureParentContainerAndReportCursor = () => {},
resetSuggestions,
}: MentionSuggestionsProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
Expand Down Expand Up @@ -149,6 +160,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe
isSuggestionPickerLarge={isMentionPickerLarge}
accessibilityLabelExtractor={keyExtractor}
measureParentContainerAndReportCursor={measureParentContainerAndReportCursor}
resetSuggestions={resetSuggestions}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ function SuggestionEmoji(
preferredSkinToneIndex={preferredSkinTone}
isEmojiPickerLarge={!!isAutoSuggestionPickerLarge}
measureParentContainerAndReportCursor={measureParentContainerAndReportCursor}
resetSuggestions={resetSuggestions}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ function SuggestionMention(
onSelect={insertSelectedMention}
isMentionPickerLarge={!!isAutoSuggestionPickerLarge}
measureParentContainerAndReportCursor={measureParentContainerAndReportCursor}
resetSuggestions={resetSuggestions}
/>
);
}
Expand Down
23 changes: 9 additions & 14 deletions src/pages/home/report/ReportActionItemMessageEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function ReportActionItemMessageEdit(
}

// Show the main composer when the focused message is deleted from another client
// to prevent the main composer stays hidden until we swtich to another chat.
// to prevent the main composer stays hidden until we switch to another chat.
setShouldShowComposeInputKeyboardAware(true);
};
},
Expand Down Expand Up @@ -354,7 +354,7 @@ function ReportActionItemMessageEdit(
const keyEvent = e as KeyboardEvent;
const isSuggestionsMenuVisible = suggestionsRef.current?.getIsSuggestionsMenuVisible();

if (isSuggestionsMenuVisible && keyEvent.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) {
if (isSuggestionsMenuVisible) {
suggestionsRef.current?.triggerHotkeyActions(keyEvent);
return;
}
Expand All @@ -374,16 +374,12 @@ function ReportActionItemMessageEdit(
[deleteDraft, hideSuggestionMenu, isKeyboardShown, isSmallScreenWidth, publishDraft],
);

const measureContainer = useCallback(
(callback: MeasureInWindowOnSuccessCallback) => {
if (!containerRef.current) {
return;
}
containerRef.current.measureInWindow(callback);
},
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
[isFocused],
);
const measureContainer = useCallback((callback: MeasureInWindowOnSuccessCallback) => {
if (!containerRef.current) {
return;
}
containerRef.current.measureInWindow(callback);
}, []);

const measureParentContainerAndReportCursor = useCallback(
(callback: MeasureParentContainerAndCursorCallback) => {
Expand All @@ -408,8 +404,7 @@ function ReportActionItemMessageEdit(

// eslint-disable-next-line react-compiler/react-compiler
tag.value = findNodeHandle(textInputRef.current) ?? -1;
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, []);
}, [tag]);
useFocusedInputHandler(
{
onSelectionChange: (event) => {
Expand Down

0 comments on commit c951669

Please sign in to comment.