Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/doomsower/react-native-modal-popover

React-Native pure JS popover that uses Modal
https://github.com/doomsower/react-native-modal-popover

modal popover react-native

Last synced: 4 days ago
JSON representation

React-Native pure JS popover that uses Modal

Awesome Lists containing this project

README

        

# react-native-modal-popover

Pure JS popover component for react-native

![Android](https://raw.githubusercontent.com/doomsower/react-native-modal-popover/master/gifs/android.gif)
![iOS](https://raw.githubusercontent.com/doomsower/react-native-modal-popover/master/gifs/ios.gif)

## About this module

The original [react-native-popover](https://github.com/jeanregisser/react-native-popover) is now outdated,
so I decided to publish my own module to avoid using github url in my package.json. Something got lost in
the process of rewriting, but now it uses `Modal` and native animation drivers, and also has cool helper
to use with Touchables. Thanks to @jeanregisser and to the authors of hanging PRs for their code.

## Requirements

Previously (version `0.0.6`) this module required react version `>16.2.0` to work (which corresponds to react-native version `>0.52.0`).

Version `0.0.7` does not reqire `React.Fragment` anymore, so you can use with reasonably old versions of react and react-native.

## Install

```sh
yarn add react-native-modal-popover
```

## Usage

This module exports two react components, `Popover` and `PopoverController`, and one react hook, `usePopover`.
`Popover` works pretty much like original `Popover`, and `PopoverController` is a convenience component that uses [React Render Props](https://github.com/jaredpalmer/awesome-react-render-props) pattern.

**Important** this example uses `React.Fragment` to wrap children, but if you use `react-native` version older than `0.52`, then you should reaplce `React.Fragment` with `View`

### Using hook

`usePopover` is preferred modern way to have popover in your app.

```jsx
import React from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import { Popover, usePopover } from 'react-native-modal-popover';

const styles = StyleSheet.create({
app: {
...StyleSheet.absoluteFillObject,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#c2ffd2',
},
content: {
padding: 16,
backgroundColor: 'pink',
borderRadius: 8,
},
arrow: {
borderTopColor: 'pink',
},
background: {
backgroundColor: 'rgba(0, 0, 255, 0.5)',
},
});

const App = () => {
const {
openPopover,
closePopover,
popoverVisible,
touchableRef,
popoverAnchorRect,
} = usePopover();
return (



Hello from inside popover!


);
};

export default App;
```

### Using PopoverController

Use `PopoverController` if you cannot use hooks for some reason.

```jsx
import React from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import { Popover, PopoverController } from 'react-native-modal-popover';

const styles = StyleSheet.create({
app: {
...StyleSheet.absoluteFillObject,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#c2ffd2',
},
content: {
padding: 16,
backgroundColor: 'pink',
borderRadius: 8,
},
arrow: {
borderTopColor: 'pink',
},
background: {
backgroundColor: 'rgba(0, 0, 255, 0.5)',
},
});

const App = () => (


{({
openPopover,
closePopover,
popoverVisible,
setPopoverAnchor,
popoverAnchorRect,
}) => (



Hello from inside popover!


)}


);

export default App;
```

## Props

### `Popover`

| Prop | Type | Optional | Default | Description |
| --------------------- | --------------------------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| visible | bool | Yes | false | Show/Hide the popover |
| fromRect | Rect | No\* | | Rectangle at which to anchor the popover. **Optional** when used inside `PopoverTouchable`, **required** when used standalone. If you set this property, you should also change it when screen orientation changes. |
| displayArea | Rect | Yes | Screen - 10px padding | Area where the popover is allowed to be displayed. **Important note:** if you use non-default value here and you want to handle screen orientation changes, it is your responsibility to change this value when screen orientation changes. |
| placement | string | Yes | 'auto' | How to position the popover - top | bottom | start | end | auto. When 'auto' is specified, it will determine the ideal placement so that the popover is fully visible within `displayArea`. |
| onClose | function | Yes | | Callback to be fired when the user closes the popover |
| onDismiss | function | Yes | | Callback to be fired after the popup closes |
| backgroundStyle | ViewStyle | Yes | | Custom style to be applied to background overlay |
| contentStyle | ViewStyle | Yes | | Custom style to be applied to popover reactangle. Use it to set round corners, background color, etc. |
| arrowStyle | ViewStyle | Yes | | Custom style to be applied to popover arrow. Use `borderTopColor` to match content backgroundColor |
| duration | number | Yes | 300 | Animation duration |
| easing | (show: boolean) => (value: number) => number | Yes | show => show ? Easing.out(Easing.back(1.70158)) : Easing.inOut(Easing.quad) | Function that returns easing function for show or hide animation, depending on `show` argument |
| useNativeDriver | bool | Yes | false | Defines if animations should use native driver |
| supportedOrientations | array of enum('portrait', 'portrait-upside-down', 'landscape', 'landscape-left', 'landscape-right') | Yes | | This prop is passed to react-native `Modal`, see [react-native docs](https://facebook.github.io/react-native/docs/modal.html#supportedorientations). Set this to `['portrait', 'landscape']` if you want your popover to resprect screen orientation. |
| calculateStatusBar | bool | Yes | false | Defines if while use status bar height while calculating "Y" origin of anchor. |

### `PopoverController` and `usePopover` hook

`PopoverController` accepts function as children. This function is called with one argument of type `PopoverControllerRenderProps` and returns react element. The children of this element are your UI handle to open popover (`Button`, `Toggle`, whatever) and `Popover` element itself. Pass properties to you handle and `Popover`, and `PopoverController` will make them work together behind the scenes. All the props are required to make controller work.

`usePopover` returns object with same props as `PopoverControllerRenderProps`, except that ref has different name: `touchableRef`.

#### `PopoverControllerRenderProps`:

| Prop | Type | Description |
| -------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| openPopover | () => void | Call this function when you want to open popover, e.g. pass to `onPress` of a `Button` |
| closePopover | () => void | Call this function when you want to close popover. Typically you pass this as `onClose` prop of `Popover`, which will make popover close when tapped outside. If you have a button inside popover which should close the popover, pass this function to this button. |
| popoverVisible | boolean | Pass this to `visible` prop of `Popover` component |
| setPopoverAnchor (`PopoverController`) / touchableRef (`usePopover`) | ref function | Pass this as `ref` to popover UI handle. This will bind popover display position to the position of this UI handle. |
| popoverAnchorRect | Rect | Pass this as `fromRect` prop of `Popover` component |

### `Rect`

Rect is an object with the following properties: `{x: number, y: number, width: number, height: number}`

## Using without `PopoverController`

In this case you have to handle refs, measure UI handle and manage popover visibility manually:

```jsx
import React from 'react';
import {
findNodeHandle,
NativeModules,
StyleSheet,
Text,
View,
} from 'react-native';
import Button from './Button';
import Popover from './popover';

const styles = StyleSheet.create({
app: {
...StyleSheet.absoluteFillObject,
padding: 10,
backgroundColor: '#c2ffd2',
alignItems: 'center',
},
});

export default class App2 extends React.Component {
state = {
showPopover: false,
popoverAnchor: { x: 0, y: 0, width: 0, height: 0 },
};

setButton = (e) => {
const handle = findNodeHandle(this.button);
if (handle) {
NativeModules.UIManager.measure(handle, (x0, y0, width, height, x, y) => {
this.setState({ popoverAnchor: { x, y, width, height } });
});
}
};

openPopover = () => {
this.setState({ showPopover: true });
};

closePopover = () => this.setState({ showPopover: false });

render() {
return (

{
this.button = r;
}}
icon="arrow-up"
onPress={this.openPopover}
onLayout={this.setButton}
/>

Hi


);
}
}
```

## Contributing

If you want to add some features, feel free to submit PR.