├── screenshots └── demo.gif ├── package.json ├── src ├── index.ios.js └── index.android.js └── README.md /screenshots/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Noitidart/react-native-popup-menu-android/HEAD/screenshots/demo.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-popup-menu-android", 3 | "version": "1.0.3", 4 | "description": "Native Module - Show the native Android material style popup menu on press of an element.", 5 | "main": "./src/", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Noitidart/react-native-popup-menu-android.git" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "android", 16 | "popup", 17 | "popover", 18 | "menu", 19 | "overlay", 20 | "native-native", 21 | "native", 22 | "material" 23 | ], 24 | "author": "Noitidart ", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/Noitidart/react-native-popup-menu-android/issues" 28 | }, 29 | "homepage": "https://github.com/Noitidart/react-native-popup-menu-android#readme" 30 | } 31 | -------------------------------------------------------------------------------- /src/index.ios.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { ActionSheetIOS } from 'react-native' 4 | 5 | type PopupMenuItem = { id?: any, label?: string } 6 | type OnPopupMenuItemSelect = (selectedPopupMenuItem: PopupMenuItem) => void 7 | type PopupMenuOptions = {| 8 | cancelLabel?: string, // defaults to "Cancel" 9 | onCancel?: () => void 10 | |} 11 | 12 | async function showPopupMenu(items: PopupMenuItem[], onSelect: OnPopupMenuItemSelect, ignoreThisIsAnchorOnAndroid, { cancelLabel='Cancel', onCancel }: PopupMenuOptions={}): Promise { 13 | // const actionTitles = this.props.hiddenButtons.map(btn => btn.props.title); 14 | // actionTitles.push(this.props.cancelButtonLabel); 15 | 16 | const actionSheetItems = items.map(item => item.label); 17 | actionSheetItems.unshift(cancelLabel); 18 | 19 | const selectedIndex: number = await new Promise(resolve => 20 | ActionSheetIOS.showActionSheetWithOptions({ 21 | options: actionSheetItems, 22 | cancelButtonIndex: 0 23 | }, resolve) 24 | ); 25 | 26 | console.log('selectedIndex:', selectedIndex); 27 | 28 | if (selectedIndex === 0) { 29 | if (onCancel) onCancel(); 30 | } else { 31 | onSelect(items[selectedIndex - 1]); 32 | } 33 | } 34 | 35 | export type { PopupMenuItem, OnPopupMenuItemSelect, PopupMenuOptions } 36 | export default showPopupMenu 37 | -------------------------------------------------------------------------------- /src/index.android.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { findNodeHandle, UIManager } from 'react-native' 4 | import { Text, TouchableNativeFeedback, TouchableWithoutFeedback, View } from 'react-native' // just for types 5 | 6 | import type { Element } from 'react' 7 | 8 | type PopupMenuItem = { id?: any, label: string } 9 | type OnPopupMenuItemSelect = (selectedPopupMenuItem: PopupMenuItem) => void 10 | type PopupAnchor = Element 11 | type PopupMenuOptions = { onCancel?: () => void } 12 | 13 | function showPopupMenu(items: PopupMenuItem[], onSelect: OnPopupMenuItemSelect, anchor: PopupAnchor, { onCancel }: PopupMenuOptions={}): void { 14 | UIManager.showPopupMenu( 15 | findNodeHandle(anchor), 16 | items.map(item => item.label), 17 | function(err) { // TODO: what are type values of err? 18 | if (onCancel) onCancel(); 19 | }, 20 | function(eventName: 'dismissed' | 'itemSelected', selectedIndex?: number) { 21 | // selectedIndex arg not present if eventName is "dismissed" 22 | if (eventName === 'itemSelected') onSelect(items[selectedIndex]); 23 | else onCancel && onCancel(); 24 | } 25 | ) 26 | } 27 | 28 | export type { PopupMenuItem, OnPopupMenuItemSelect, PopupMenuOptions } 29 | export default showPopupMenu 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-popup-menu-android 2 | 3 | ![](https://github.com/Noitidart/react-native-popup-menu-android/blob/master/screenshots/demo.gif) 4 | 5 | Show the native Android material style popup menu on press of an element. This react-native module is a "Native Module" type. However you do not have to link because this is already available inside React Native. 6 | 7 | #### Table of Contents 8 | 9 | - [Demo](#demo) 10 | - [Usage](#usage) 11 | - [Installation](#installation) 12 | - [Import](#import) 13 | - [Render](#render) 14 | - [API](#api) 15 | - [`showPopupMenu`](#showpopupmenu) 16 | - [Types](#types) 17 | - [Exported Types](#exported-types) 18 | - [`type PopupMenuItem`](#type-popupmenuitem) 19 | - [`type PopupMenuOptions`](#type-popupmenuoptions) 20 | - [Android](#android) 21 | - [iOS](#ios) 22 | - [`type OnPopupMenuItemSelect`](#type-onpopupmenuitemselect) 23 | - [Internal Types](#internal-types) 24 | - [`type PopupAnchor`](#type-popupanchor) 25 | - [Similar Packages](#similar-packages) 26 | 27 | ### Demo 28 | 29 | A demo of the screen recording shown above is available as a Snack: 30 | 31 | [snack.expo.io :: react-native-popup-menu-android Demo](https://snack.expo.io/@noitsnack/react-native-popup-menu-android-demo) 32 | 33 | ### Usage 34 | 35 | #### Installation 36 | 37 | npm i react-native-popup-menu-android 38 | 39 | #### Import 40 | 41 | import showPopupMenu from 'react-native-popup-menu-android' 42 | 43 | #### Render 44 | 45 | class More extends Component<||> { 46 | moreButton: null | Element 47 | 48 | render() { 49 | return ( 50 | 51 | 52 | Show Menu 53 | 54 | 55 | ) 56 | } 57 | 58 | refMoreButton = el => this.moreButton = el 59 | 60 | showMore = () => { 61 | showPopupMenu( 62 | [ 63 | { id:'edit', label:'Quick Edit' }, 64 | { id:'delete', label:'Trash' }, 65 | { id:'follow', label:'Follow' } 66 | ], 67 | this.handleMoreItemSelect, 68 | this.moreButton 69 | ); 70 | } 71 | 72 | handleMoreItemSelect = (item: PopupMenuItem) => { 73 | alert('Pressed: ' + item.label) 74 | } 75 | } 76 | 77 | 78 | ### API 79 | 80 | #### `showPopupMenu` 81 | 82 | > static showPopupMenu( 83 | > items: PopupMenuItem, 84 | > onSelect: OnPopupMenuItemSelect, 85 | > anchor: PopupMenuAchor, 86 | > options: PopupMenuOptions 87 | > ): void 88 | 89 | Show the native popup menu relative to element referenced by `anchor`. 90 | 91 | | Parameter | Type | Default | Required | Description | 92 | |-----------|--------------------------------------------------------|-------------|----------|-------------------------------------------------------------------| 93 | | items | [`PopupMenuItem`](#type-popupmenuitem)[] | | Yes | Array of strings that are offered as autocomplete suggestions. | 94 | | onSelect | [`OnPopupMenuItemSelect`](#type-onpopupmenuitemselect) | | Yes | A callback that gets | 95 | | anchor | [`PopupAnchor`](#type-popupanchor) | | Yes | A ref to the element from which the menu should be positioned on. | 96 | | options | [`PopupOptions`](#type-popupoptions) | `undefined` | | See [`PopupOptions`](#type-popupoptions). | 97 | 98 | ### Types 99 | 100 | [Flow](http://flow.org/) is used as the typing system. 101 | 102 | #### Exported Types 103 | 104 | import type { 105 | OnPopupMenuItemSelect, 106 | PopupMenuItem, 107 | PopupMenuOptions 108 | } from 'react-native-popup-android' 109 | 110 | ##### `type OnPopupMenuItemSelect` 111 | 112 | > (selectedPopupMenuItem: [PopupMenuItem](#type-popupmenuitem)) => void 113 | 114 | ##### `type PopupMenuItem` 115 | 116 | > { id?: any, label: string } 117 | 118 | ##### `type PopupMenuOptions` 119 | 120 | ###### Android 121 | 122 | > { 123 | > onCancel?: () => void 124 | > } 125 | 126 | | Key | Type | Default | Required | Description | 127 | |----------|--------------|-------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 128 | | onCancel | `() => void` | `undefined` | | A callback that triggered by user when he dismisses the popup due to (1) hits the hardware back button, or (2) presses outside of the popup menu. This callback is also triggered if an error occurs in trying to display the popup. | 129 | 130 | ###### iOS 131 | 132 | TODO 133 | 134 | 135 | #### Internal Types 136 | 137 | ##### `type PopupAnchor` 138 | 139 | > type PopupAnchor = Element< 140 | > typeof Text | 141 | > typeof TouchableNativeFeedback | 142 | > typeof TouchableWithoutFeedback | 143 | > typeof View 144 | > > 145 | 146 | ### Similar Packages 147 | 148 | * [**react-native-popover-menu**](https://github.com/prscX/react-native-popover-menu) - Also shows a native popup menu. Includes iOS style popover. 149 | * [**``**](https://github.com/facebook/react-native/pull/14581#issue-126176285) - Component provided out of the box in React Native. 150 | * [**react-native-material-menu**](https://github.com/mxck/react-native-material-menu) - Gives the "desktop" material style menu. This is not a native module, it is a javascript implementation. 151 | 152 | --------------------------------------------------------------------------------