Zeego

Menus for React (Native) done right.

README

Frame 32

Beautiful, native menus for React Native + Web, inspired by Radix UI.

- 🕺 Radix UI on Web
- 🛫 Native elements on iOS/Android (where possible)
- 🌲 Same API cross-platform
- 🌊 Works with Solito and Next.js
- 🤖 Supports Expo (with custom dev clients)
- 🍦 Vanilla React Native too
- 🎨 100% unstyled components

About Zeego


To learn more about Zeego and its approach to menus, you can watch Fernando Rojo's talk at App.js Conf.

The Zeego release happens at about 9:35.

Screen Shot 2022-08-29 at 8 43 30 PM


Installation


  1. ```sh
  2. yarn add zeego
  3. ```

Install peer dependencies


If you're in a monorepo, you should install these in the directory of your native app.

iOS


  1. ```sh
  2. yarn add react-native-ios-context-menu
  3. ```

Android


  1. ```sh
  2. yarn add @react-native-menu/menu
  3. ```

Solito


As shown above, sure to install react-native-ios-context-menu and @react-native-menu/menu in your Expo folder (apps/expo).

You should also follow the Next.js steps below.

Next.js


You need to add zeego to your next-transpile-modules in next.config.js.

Expo


You need to use a custom development client, since react-native-ios-context-menu uses native code.

After installing, you'll need to rebuild your custom development client and app.

Vanilla React Native


Run pod install in your ios folder.

Philosophy


Optimized

1. Use the best API possible
2. Create the best experience for each platform, without concern for sharing styles
3. Rely on built-in native menus for iOS and Android
4. Everything ships unstyled

The API follows that of Radix UI closely.

Usage


  1. ```ts
  2. import * as DropdownMenu from 'zeego/dropdown-menu'
  3. ```

See radix-ui's dropdown menu. It's really similar.

One difference is that DropdownMenu.Item needs a child DropdownMenu.ItemTitle, since React Native separates Text and View components.

  1. ```tsx
  2. <DropdownMenu.Item>
  3.   <DropdownMenu.ItemTitle>Bookmark</DropdownMenu.ItemTitle>
  4.   <DropdownMenu.ItemIcon iosIconName="bookmark">
  5.     <YourIconComponent />
  6.   </DropdownMenu.ItemIcon>
  7. </DropdownMenu.Item>
  8. ```

Custom components


To use a custom component, you'll first need to menuify it.

Here is an example of a custom component using Dripsy:

  1. ```tsx
  2. import * as DropdownMenu from 'zeego/dropdown-menu'
  3. import { styled } from 'dripsy'

  4. const StyledMenuItem = styled(DropdownMenu.Item)({
  5.   height: 32,
  6. })

  7. // this part is important
  8. const DripsyMenuItem = DropdownMenu.menuify(StyledMenuItem, 'Item')
  9. ```

And now, you can use it:

  1. ```tsx
  2. <DripsyMenuItem key="fernando">
  3.   <DropdownMenu.ItemTitle>Fernando</DropdownMenu.ItemTitle>
  4. <DripsyMenuItem>
  5. ```

Under the hood, menuify applies a displayName to your component. This allows zeego to recognize it when it's mapping children for iOS and Android.

Example


For now, you should reference the example in the repo.

I also added a Moti + Dripsy example.

In the future, I'll make an example app with Solito too.

  1. ```tsx
  2. const DropdownMenuExample = () => {
  3.   const [bookmarked, setBookmarked] = useState<'on' | 'off' | 'mixed'>('on')
  4.   return (
  5.     <DropdownMenu.Root>
  6.       <DropdownMenu.Trigger>
  7.         <View>
  8.           <Text style={styles.button}>{`<DropdownMenu />`}Text>
  9.         </View>
  10.       </DropdownMenu.Trigger>
  11.       <DropdownMenu.Content style={dropdownStyles.content}>
  12.         <DropdownMenuLabel>Help</DropdownMenuLabel>
  13.         <DropdownMenuItem
  14.           style={dropdownStyles.item}
  15.           onSelect={select(1)}
  16.           key="first"
  17.         >
  18.           <DropdownMenuItemTitle style={dropdownStyles.itemTitle}>
  19.             See more
  20.           </DropdownMenuItemTitle>
  21.           {Platform.OS === 'ios' && (
  22.             <DropdownMenu.ItemSubtitle style={dropdownStyles.itemSubtitle}>
  23.               12 artists fit your search
  24.             </DropdownMenu.ItemSubtitle>
  25.           )}
  26.           <DropdownMenuItemIcon iosIconName="list.star" androidIconName="star_on">
  27.             <Ionicons name="list" size={15} />
  28.           </DropdownMenuItemIcon>
  29.         </DropdownMenuItem>
  30.         <DropdownMenuItem
  31.           style={dropdownStyles.item}
  32.           onSelect={select(2)}
  33.           key="second"
  34.         >
  35.           <DropdownMenuItemTitle>Favorite</DropdownMenuItemTitle>
  36.           <DropdownMenuItemIcon iosIconName="star.fill" androidIconName="star_off">
  37.             <Ionicons name="star" size={15} />
  38.           </DropdownMenuItemIcon>
  39.         </DropdownMenuItem>
  40.         <DropdownMenuCheckboxItem
  41.           style={dropdownStyles.item}
  42.           value={bookmarked}
  43.           onValueChange={setBookmarked}
  44.           key="third"
  45.         >
  46.           <DropdownMenuItemIndicator>
  47.             <Ionicons name="checkmark" size={19} />
  48.           </DropdownMenuItemIndicator>
  49.           <DropdownMenuItemTitle>
  50.             {bookmarked === 'on' ? 'Bookmarked' : 'Bookmark'}
  51.           </DropdownMenuItemTitle>
  52.           <DropdownMenuItemImage
  53.             iosIconName="book"
  54.             source={require('./camera-outline.png')}
  55.             width={20}
  56.             resizeMode="contain"
  57.           />
  58.         </DropdownMenuCheckboxItem>
  59.       </DropdownMenu.Content>
  60.     </DropdownMenu.Root>
  61.   )
  62. }
  63. ```

TODO


- [x] zeego/dropdown-menu
- [ ] zeego/context-menu (in-progress, missing Web implementation)
- [x] Android native implementation
- [ ] Docs
- [ ] zeego/tooltip (probably?)
- [ ] zeego/popover (probably)

Thanks


Special thanks to Dominic Go for the awesome iOS context menu. Also, thanks to the Showtime team for testing this and sending PRs.