Skip to content

Commit 5b7e7b9

Browse files
authored
Merge pull request Expensify#87155 from Expensify/shawn-compactMenus
[INTERNAl QA] Compact row styles for context menus and popovers
2 parents f401344 + fc006fc commit 5b7e7b9

12 files changed

Lines changed: 342 additions & 124 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {createContext, useContext} from 'react';
2+
3+
const CompactMenuContext = createContext(false);
4+
5+
function useIsCompactMenu() {
6+
return useContext(CompactMenuContext);
7+
}
8+
9+
export default CompactMenuContext;
10+
export {useIsCompactMenu};

src/components/MenuItem.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type WithSentryLabel from '@src/types/utils/SentryLabel';
3232
import ActivityIndicator from './ActivityIndicator';
3333
import Avatar from './Avatar';
3434
import Badge from './Badge';
35+
import {useIsCompactMenu} from './CompactMenuContext';
3536
import CopyTextToClipboard from './CopyTextToClipboard';
3637
import DisplayNames from './DisplayNames';
3738
import type {DisplayNameWithTooltip} from './DisplayNames/types';
@@ -114,6 +115,9 @@ type MenuItemBaseProps = ForwardedFSClassProps &
114115
/** Styles to apply on the title wrapper */
115116
titleWrapperStyle?: StyleProp<ViewStyle>;
116117

118+
/** Styles to apply on the inner row containing the icon and text content */
119+
innerContainerStyle?: StyleProp<ViewStyle>;
120+
117121
/** Any additional styles to apply on the outer element */
118122
containerStyle?: StyleProp<ViewStyle>;
119123

@@ -482,6 +486,7 @@ function MenuItem({
482486
style,
483487
wrapperStyle,
484488
titleWrapperStyle,
489+
innerContainerStyle,
485490
outerWrapperStyle,
486491
containerStyle,
487492
titleStyle,
@@ -608,11 +613,15 @@ function MenuItem({
608613
const styles = useThemeStyles();
609614
const StyleUtils = useStyleUtils();
610615
const combinedStyle = [styles.popoverMenuItem, style];
611-
const {shouldUseNarrowLayout} = useResponsiveLayout();
616+
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
617+
const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout();
612618
const {isExecuting} = useMenuItemGroupState() ?? {};
613619
const {singleExecution, waitForNavigate} = useMenuItemGroupActions() ?? {};
614620
const popoverAnchor = useRef<View>(null);
615621
const deviceHasHoverSupport = hasHoverSupport();
622+
const isCompactMenu = useIsCompactMenu();
623+
const isCompactPopoverItem = isCompactMenu && !isSmallScreenWidth;
624+
const compactIconStyle = isCompactPopoverItem && iconType === CONST.ICON_TYPE_ICON && {width: variables.iconSizeNormal};
616625
const isCompact = viewMode === CONST.OPTION_MODE.COMPACT;
617626
const isDeleted = style && Array.isArray(style) ? style.includes(styles.offlineFeedbackDeleted) : false;
618627
const descriptionVerticalMargin = shouldShowDescriptionOnTop ? styles.mb1 : styles.mt1;
@@ -837,6 +846,7 @@ function MenuItem({
837846
!interactive && styles.cursorDefault,
838847
isCompact && styles.alignItemsCenter,
839848
isCompact && styles.optionRowCompact,
849+
isCompactPopoverItem && (description ? styles.compactPopoverMenuItemBase : styles.compactPopoverMenuItem),
840850
!shouldRemoveBackground &&
841851
StyleUtils.getButtonBackgroundColorStyle(getButtonState(focused || isHovered, pressed, success, disabled, interactive), true),
842852
...(Array.isArray(wrapperStyle) ? wrapperStyle : [wrapperStyle]),
@@ -868,7 +878,15 @@ function MenuItem({
868878
</Text>
869879
</View>
870880
)}
871-
<View style={[styles.flexRow, styles.pointerEventsAuto, disabled && !shouldUseDefaultCursorWhenDisabled && styles.cursorDisabled]}>
881+
<View
882+
style={[
883+
styles.flexRow,
884+
styles.pointerEventsAuto,
885+
disabled && !shouldUseDefaultCursorWhenDisabled && styles.cursorDisabled,
886+
isCompactPopoverItem && styles.alignItemsCenter,
887+
innerContainerStyle,
888+
]}
889+
>
872890
{!!leftComponent && <View style={[styles.mr3]}>{leftComponent}</View>}
873891
{/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */}
874892
{isIDPassed && (
@@ -891,6 +909,7 @@ function MenuItem({
891909
styles.popoverMenuIcon,
892910
iconStyles,
893911
shouldIconUseAutoWidthStyle ? styles.wAuto : StyleUtils.getAvatarWidthStyle(avatarSize),
912+
compactIconStyle,
894913
]}
895914
/>
896915
)}
@@ -900,6 +919,7 @@ function MenuItem({
900919
styles.popoverMenuIcon,
901920
iconStyles,
902921
shouldIconUseAutoWidthStyle ? styles.wAuto : StyleUtils.getAvatarWidthStyle(avatarSize),
922+
compactIconStyle,
903923
]}
904924
>
905925
{typeof icon !== 'string' &&

src/components/PopoverMenu.tsx

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
1313
import useStyleUtils from '@hooks/useStyleUtils';
1414
import useTheme from '@hooks/useTheme';
1515
import useThemeStyles from '@hooks/useThemeStyles';
16+
import useWindowDimensions from '@hooks/useWindowDimensions';
1617
import {isSafari} from '@libs/Browser';
1718
import getPlatform from '@libs/getPlatform';
1819
import variables from '@styles/variables';
@@ -22,10 +23,11 @@ import type {AnchorPosition} from '@src/styles';
2223
import type {PendingAction} from '@src/types/onyx/OnyxCommon';
2324
import type AnchorAlignment from '@src/types/utils/AnchorAlignment';
2425
import type IconAsset from '@src/types/utils/IconAsset';
26+
import CompactMenuContext from './CompactMenuContext';
2527
import FocusableMenuItem from './FocusableMenuItem';
2628
import FocusTrapForModal from './FocusTrap/FocusTrapForModal';
27-
import type {MenuItemProps} from './MenuItem';
2829
import MenuItem from './MenuItem';
30+
import type {MenuItemProps} from './MenuItem';
2931
import type ReanimatedModalProps from './Modal/ReanimatedModal/types';
3032
import type BaseModalProps from './Modal/types';
3133
import OfflineWithFeedback from './OfflineWithFeedback';
@@ -312,6 +314,7 @@ function BasePopoverMenu({
312314
// We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to apply correct popover styles
313315
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
314316
const {isSmallScreenWidth, isInLandscapeMode} = useResponsiveLayout();
317+
const {windowHeight} = useWindowDimensions();
315318
const [currentMenuItems, setCurrentMenuItems] = useState(menuItems);
316319
const currentMenuItemsFocusedIndex = getSelectedItemIndex(currentMenuItems);
317320
const [enteredSubMenuIndexes, setEnteredSubMenuIndexes] = useState<readonly number[]>(CONST.EMPTY_ARRAY);
@@ -374,7 +377,10 @@ function BasePopoverMenu({
374377
icon={expensifyIcons.BackArrow}
375378
iconFill={(isHovered) => (isHovered ? theme.iconHovered : theme.icon)}
376379
style={hasBackButtonText ? styles.pv0 : undefined}
377-
additionalIconStyles={[{width: variables.iconSizeSmall, height: variables.iconSizeSmall}, styles.opacitySemiTransparent, styles.mr1]}
380+
additionalIconStyles={[{width: variables.iconSizeNormal, height: variables.iconSizeNormal}, styles.opacitySemiTransparent, styles.mr1]}
381+
iconStyles={[{width: variables.iconSizeNormal, height: variables.iconSizeNormal}]}
382+
wrapperStyle={[styles.ph5, styles.pv3]}
383+
innerContainerStyle={styles.alignItemsCenter}
378384
title={backButtonTitle}
379385
accessibilityLabel={`${translate('common.goBack')}, ${backButtonTitle}`}
380386
titleStyle={hasBackButtonText ? styles.createMenuHeaderText : undefined}
@@ -547,16 +553,17 @@ function BasePopoverMenu({
547553
return shouldEnableMaxHeight && !isInLandscapeMode ? [{maxHeight: CONST.POPOVER_MENU_MAX_HEIGHT_MOBILE}] : [];
548554
}
549555

550-
const stylesArray: ViewStyle[] = [StyleSheet.flatten(styles.createMenuContainer)];
556+
const stylesArray: ViewStyle[] = [StyleSheet.flatten(styles.createMenuContainer), {width: variables.compactPopoverMenuWidth}, styles.pv2];
551557

552558
if (shouldUseScrollView && shouldEnableMaxHeight && !isInLandscapeMode) {
553-
stylesArray.push({maxHeight: CONST.POPOVER_MENU_MAX_HEIGHT});
559+
stylesArray.push({maxHeight: Math.max(windowHeight - variables.compactPopoverMenuVerticalMargin, CONST.POPOVER_MENU_MAX_HEIGHT)});
554560
}
555561

556562
return stylesArray;
557-
}, [isSmallScreenWidth, shouldEnableMaxHeight, styles.createMenuContainer, shouldUseScrollView]);
563+
}, [isSmallScreenWidth, shouldEnableMaxHeight, styles.createMenuContainer, styles.pv2, shouldUseScrollView, windowHeight, isInLandscapeMode]);
558564

559-
const {paddingTop, paddingBottom, paddingVertical, ...restScrollContainerStyle} = (StyleSheet.flatten([styles.pv4, scrollContainerStyle]) as ViewStyle) ?? {};
565+
const {paddingTop, paddingBottom, paddingVertical, ...restScrollContainerStyle} =
566+
(StyleSheet.flatten([isSmallScreenWidth ? styles.pv4 : styles.pv2, scrollContainerStyle]) as ViewStyle) ?? {};
560567
const {
561568
paddingVertical: menuContainerPaddingVertical,
562569
paddingTop: menuContainerPaddingTop,
@@ -624,16 +631,18 @@ function BasePopoverMenu({
624631
active={isVisible}
625632
shouldReturnFocus={!shouldEnableNewFocusManagement}
626633
>
627-
<View
628-
onLayout={onLayout}
629-
style={[restMenuContainerStyle, restContainerStyles, isWeb ? styles.flex1 : styles.flexGrow1]}
630-
>
631-
{renderWithConditionalWrapper(
632-
shouldUseScrollView,
633-
[scrollViewPaddingStyles, restScrollContainerStyle],
634-
[renderHeaderText(), enteredSubMenuIndexes.length > 0 && renderBackButtonItem(), renderedMenuItems],
635-
)}
636-
</View>
634+
<CompactMenuContext.Provider value>
635+
<View
636+
onLayout={onLayout}
637+
style={[restMenuContainerStyle, restContainerStyles, isWeb ? styles.flex1 : styles.flexGrow1]}
638+
>
639+
{renderWithConditionalWrapper(
640+
shouldUseScrollView,
641+
[scrollViewPaddingStyles, restScrollContainerStyle],
642+
[renderHeaderText(), enteredSubMenuIndexes.length > 0 && renderBackButtonItem(), renderedMenuItems],
643+
)}
644+
</View>
645+
</CompactMenuContext.Provider>
637646
</FocusTrapForModal>
638647
</PopoverWithMeasuredContent>
639648
);

src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import EmojiReactionBubble from '@components/Reactions/EmojiReactionBubble';
66
import Tooltip from '@components/Tooltip';
77
import useLocalize from '@hooks/useLocalize';
88
import useOnyx from '@hooks/useOnyx';
9+
import useResponsiveLayout from '@hooks/useResponsiveLayout';
910
import useThemeStyles from '@hooks/useThemeStyles';
1011
import {getLocalizedEmojiName, getPreferredEmojiCode} from '@libs/EmojiUtils';
1112
import {callFunctionIfActionIsAllowed} from '@userActions/Session';
@@ -25,6 +26,7 @@ function BaseQuickEmojiReactions({
2526
}: BaseQuickEmojiReactionsProps) {
2627
const styles = useThemeStyles();
2728
const {preferredLocale} = useLocalize();
29+
const {shouldUseNarrowLayout} = useResponsiveLayout();
2830
const [preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE);
2931
const [emojiReactions = getEmptyObject<ReportActionReactions>()] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`);
3032

@@ -36,7 +38,7 @@ function BaseQuickEmojiReactions({
3638
);
3739

3840
return (
39-
<View style={styles.quickReactionsContainer}>
41+
<View style={[styles.quickReactionsContainer, !shouldUseNarrowLayout && styles.compactQuickReactionsContainer]}>
4042
{CONST.QUICK_REACTIONS.map((emoji: Emoji) => (
4143
<Tooltip
4244
text={`:${getLocalizedEmojiName(emoji.name, preferredLocale)}:`}

src/components/Search/FilterDropdowns/DisplayPopup.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, {useState} from 'react';
22
import type {OnyxEntry} from 'react-native-onyx';
3+
import CompactMenuContext from '@components/CompactMenuContext';
34
import MenuItem from '@components/MenuItem';
45
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
56
import ScrollView from '@components/ScrollView';
@@ -116,15 +117,17 @@ function DisplayPopup({queryJSON, searchResults, closeOverlay, onSort}: DisplayP
116117
/>
117118
)}
118119
{shouldShowColumnsButton && (
119-
<MenuItem
120-
icon={expensifyIcons.Columns}
121-
title={translate('search.editColumns')}
122-
onPress={() => {
123-
closeOverlay();
124-
openSearchColumns();
125-
}}
126-
sentryLabel={CONST.SENTRY_LABEL.SEARCH.COLUMNS_BUTTON}
127-
/>
120+
<CompactMenuContext.Provider value>
121+
<MenuItem
122+
icon={expensifyIcons.Columns}
123+
title={translate('search.editColumns')}
124+
onPress={() => {
125+
closeOverlay();
126+
openSearchColumns();
127+
}}
128+
sentryLabel={CONST.SENTRY_LABEL.SEARCH.COLUMNS_BUTTON}
129+
/>
130+
</CompactMenuContext.Provider>
128131
)}
129132
</ScrollView>
130133
);

src/pages/ReferralDetailsPage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ function ReferralDetailsPage({route}: ReferralDetailsPageProps) {
7777
successIcon={icons.Checkmark}
7878
successText={translate('qrCodes.copied')}
7979
onPress={() => Clipboard.setString(referralLink)}
80+
shouldLimitWidth={false}
8081
/>
8182
)}
8283

0 commit comments

Comments
 (0)