@react-native-menu/menu

repository·master·Indexed 22 days ago

https://github.com/react-native-menu/menu

A React Native library providing native menu components, including Android's PopupMenu and iOS 14+ UIMenu, with a fallback to ActionSheet for older iOS versions. It features the MenuView component for wrapping trigger views, support for nested subactions, and platform-specific configurations for titles, images, and theme variants. Version 2.0.0.

Tokens
3.5K
Snippets
5
Records
16
Agent score
79%

What's inside @react-native-menu/menu

  1. Use custom icons on Android

    master

    To use custom XML drawable icons in MenuAction.image on Android:

    1. Prepare the icon: Download an XML drawable (e.g., from Material Icons).
    2. Add to project:
      • Bare React Native: Copy the .xml file to android/app/src/main/res/drawable.
      • Expo: Use an expo config plugin to copy files from assets to the drawable folder. Example app.json config:
        {
          "expo": {
            "plugins": [
              [
                "./plugin/withAndroidDrawables",
                {
                  "drawableFiles": [ "./assets/my_icon.xml" ]
                }
              ]
            ]
          }
        }
    3. Reference in code: Use the filename without the extension: image: 'my_icon'
    4. Rebuild: Run a new build to include the new resources.
  2. Mock MenuView for Jest testing

    master

    If you need to test components that use @react-native-menu/menu, you can mock the MenuView to simulate action presses using jest.mock.

    import type { MenuComponentProps } from '@react-native-menu/menu';
    
    jest.mock('@react-native-menu/menu', () => ({
      MenuView: jest.fn((props: MenuComponentProps) => {
        const React = require('react');
    
        class MockMenuView extends React.Component {
          render() {
            return React.createElement(
              'View',
              {
                testID: props.testID,
              },
              // Dynamically mock each action
              props.actions.map(action =>
                React.createElement('Button', {
                  key: action.id,
                  title: action.title,
                  onPress: () => {
                    if (action.id && props?.onPressAction) {
                      props.onPressAction({ nativeEvent: { event: action.id } });
                    }
                  },
                  testID: action.id
                })
              ),
              this.props.children
            );
          }
        }
    
        return React.createElement(MockMenuView, props);
      })
    }));
  3. Use MenuView to display a menu

    master

    The MenuView component wraps a child view (the trigger) and displays a native menu when interacted with. You can control the menu imperatively using a MenuComponentRef (Android only) or via the default interaction behavior.

    import { MenuView, MenuComponentRef } from '@react-native-menu/menu';
    
    const App = () => {
      const menuRef = useRef<MenuComponentRef>(null);
      return (
        <View style={styles.container}>
          <Button
            title="Show Menu with ref (Android only)"
            onPress={() => menuRef.current?.show()}
          />
          <MenuView
            ref={menuRef}
            title="Menu Title"
            onPressAction={({ nativeEvent }) => {
              console.warn(JSON.stringify(nativeEvent));
            }}
            actions={[
              {
                id: 'add',
                title: 'Add',
                titleColor: '#2367A2',
                image: Platform.select({
                  ios: 'plus',
                  android: 'ic_menu_add',
                }),
                imageColor: '#2367A2',
                subactions: [
                  {
                    id: 'nested1',
                    title: 'Nested action',
                    titleColor: 'rgba(250,180,100,0.5)',
                    subtitle: 'State is mixed',
                    image: Platform.select({
                      ios: 'heart.fill',
                      android: 'ic_menu_today',
                    }),
                    imageColor: 'rgba(100,200,250,0.3)',
                    state: 'mixed',
                  },
                  {
                    id: 'nestedDestructive',
                    title: 'Destructive Action',
                    attributes: {
                      destructive: true,
                    },
                    image: Platform.select({
                      ios: 'trash',
                      android: 'ic_menu_delete',
                    }),
                  },
                ],
              },
              {
                id: 'share',
                title: 'Share Action',
                titleColor: '#46F289',
                subtitle: 'Share action on SNS',
                image: Platform.select({
                  ios: 'square.and.arrow.up',
                  android: 'ic_menu_share',
                }),
                imageColor: '#46F289',
                state: 'on',
              },
              {
                id: 'destructive',
                title: 'Destructive Action',
                attributes: {
                  destructive: true,
                },
                image: Platform.select({
                  ios: 'trash',
                  android: 'ic_menu_delete',
                }),
              },
            ]}
            shouldOpenOnLongPress={false}
          >
            <View style={styles.button}>
              <Text style={styles.buttonText}>Test</Text>
            </View>
          </MenuView>
        </View>
      );
    };
  4. Configure autolinking for @react-native-menu/menu

    master

    To ensure the react-native-cli can correctly find and autolink the @react-native-menu/menu library, the package must be defined in the react-native.config.js file with its root directory pointing to the library's root path.

    module.exports = {
    	dependencies: {
    		// Help rn-cli find and autolink this library
    		"@react-native-menu/menu": {
    			root: __dirname,
    		},
    	},
    };
  5. Configure MenuAttributes

    master

    Use MenuAttributes to define the visual style of a MenuAction.

    type MenuAttributes = {
      /**
       * An attribute indicating the destructive style.
       */
      destructive?: boolean;
      /**
       * An attribute indicating the disabled style.
       */
      disabled?: boolean;
      /**
       * An attribute indicating the hidden style.
       */
      hidden?: boolean;
    };
  6. Configure MenuView props

    master

    The MenuView component accepts several props to customize its behavior and appearance across platforms.

    PropTypeRequiredDescription
    refrefNoRef to the menu component (Android only).
    titlestringYes (iOS)The title of the menu (iOS only).
    isAnchoredToRightbooleanNo (Android)Determines if menu should be anchored to right or left corner of parent view (Android only).
    shouldOpenOnLongPressbooleanNoDetermines if menu should open after long press or on normal press.
    actionsMenuAction[]YesActions to be displayed in the menu.
    themeVariantenum('light', 'dark')No (iOS)Overrides the theme of the menu (iOS only).
    onPressAction({nativeEvent}) => voidNoCallback function called when selecting a menu item. Contains the id of the action.
    onOpenMenu() => voidNoCallback called right before the menu is displayed.
    onCloseMenu() => voidNoCallback called at the start of dismissal, before animations complete.
  7. Define MenuAction objects

    master

    A MenuAction object defines an individual item within the menu.

    PropertyTypePlatformDescription
    idstringAllIdentifier of the menu action. Returned in onPressAction.
    titlestringAllThe action's title.
    titleColornumber | ColorValueAndroidThe action's title color.
    subtitlestringiOS 14+An elaborated title explaining the purpose.
    attributesMenuAttributesAllStyle attributes like destructive, disabled, or hidden.
    state'off' | 'on' | 'mixed'iOS 14+The state of the action.
    imagestringAndroid/iOS 13+Icon name (SF Symbol for iOS, drawable resource for Android).
    imageColornumber | ColorValueAndroid/iOS 13+The action's image color.
    subactionsMenuAction[]Android/iOS 14+Nested actions. Note: Android does not support nesting sub-menus within sub-menu items.
  8. Use the MenuView component

    master
    The MenuView component is the primary entry point for rendering a native menu in your React Native application. It accepts an array of actions and can be controlled via props like hitSlop. It supports forwardRef, allowing you to access the MenuComponentRef to interact with the native menu instance.
  9. Reference: MenuAction attributes and state

    master

    The MenuAttributes object allows you to style individual actions:

    • destructive: Boolean indicating a destructive style.
    • disabled: Boolean indicating a disabled style.
    • hidden: Boolean indicating a hidden style.
    • keepsMenuPresented: (iOS 16+ only) Indicates if the menu should remain presented after firing.

    MenuState (iOS 14+ only) defines the selection state:

    • off
    • on
    • mixed