React Floater
===

[![NPM version](https://badge.fury.io/js/react-floater.svg)](https://www.npmjs.com/package/react-floater)
[![build status](https://travis-ci.org/gilbarbara/react-floater.svg)](https://travis-ci.org/gilbarbara/react-floater)
[![Maintainability](https://api.codeclimate.com/v1/badges/a3457f536c0915c0935b/maintainability)](https://codeclimate.com/github/gilbarbara/react-floater/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/a3457f536c0915c0935b/test_coverage)](https://codeclimate.com/github/gilbarbara/react-floater/test_coverage)

### View the [demo](https://d0pt7.csb.app)
You can view and edit the code for each Floater [here](https://codesandbox.io/s/github/gilbarbara/react-floater-demo)

## Usage

Install.

```bash
npm install --save react-floater
```

Import it into your component:

```jsx
import Floater from 'react-floater';

<Floater content="This is the Floater content">
    <span>click me</span>
</Floater>

```

And voíla!

## Customization

You can use your own components to render the Floater with the prop `component`.  
Check `WithStyledComponents.js` in the [demo](https://84vn36m178.codesandbox.io/) for an example.

If you use your own components as `children` it will receive an `innerRef` prop that you must set in your HTMLElement:  
*Stateless components don't accept refs...*

```jsx
const Button = ({ innerRef, ...rest }) => (
  <button ref={innerRef} {...rest} />
);

...
<Floater content="This is the Floater content">
    <Button>click me</Button>
</Floater>
```

This works transparently with styled-components (and possible other modules):  

```jsx
const Wrapper = styled.div`
  margin: 0 auto;
  max-width: 500px;
  line-height: 1.5;
`;

<Floater content="This is the Floater content">
    <Wrapper>click me</Wrapper>
</Floater>

```

## Props

**autoOpen** {bool} ▶︎ `false`  
Open the Floater automatically.

**callback** {func}  
It will be called when the Floater change state with 2 parameters:

- **action** {string} `open` or `close`  
- **props** {object} the props you passed.

**children** {node}  
An element to trigger the Floater.

**component** {element|function}  
A React component or function to as a custom UI for the Floater.  
The prop `closeFloater` will be available in your component.

**content** {node}  
The Floater content. It can be anything that can be rendered.  
*This is the only required props, unless you pass a* `component`.

**debug** {bool} ▶︎ `false`  
Log some basic actions.  
*You can also set a global variable* `ReactFloaterDebug = true;`

**disableAnimation** {bool} ▶︎ `false`  
Animate the Floater on scroll/resize.

**disableFlip** {bool} ▶︎ `false`  
Disable changes in the Floater position on scroll/resize.

**disableHoverToClick** {bool} ▶︎ `false`  
Don't convert *hover* event to *click* on mobile.

**event** {string} ▶︎ `click`  
The event that will trigger the Floater. It can be `hover | click`.  
*These won't work in controlled mode.*

**eventDelay** {number} ▶︎ `0.4`  
The amount of time (in seconds) that the floater should wait after a `mouseLeave` event before hiding.  
Only valid for event type `hover`.

**footer** {node}  
It can be anything that can be rendered.

**getPopper** {function}
Get the popper.js instance. It receives with 2 parameters:

- **popper** {object} the popper object  
- **origin** {object} `floater` or `wrapper`

**hideArrow** {bool} ▶︎ `false`  
Don't show the arrow. Useful for centered or modal layout.

**id** {string|number}  
In case that you need to identify the portal.

**offset** {number} ▶︎ `15`  
The distance between the Floater and its target in pixels.

**open** {bool} ▶︎ `false`  
The switch between normal and controlled modes.  
*Setting this prop will disabled the normal behavior.*

**options** {object}  
Customize popper.js modifiers.  
*Don't use it unless you know what you're doing*

**placement** {string} ▶︎ `bottom`  
The placement of the Floater. It will update the position if there's no space available.

It can be:

- top (top-start, top-end)
- bottom (bottom-start, bottom-end)
- left (left-start, left-end)
- right (right-start, right-end
- auto
- center

**showCloseButton** {bool} ▶︎ `false`  
It will show a ⨉ button to close the Floater.  
This will be `true` when you change `wrapperOptions` position.

**styles** {object} ▶︎ `defaultStyles`  
Customize the default UI.

**target** {object|string}  
The target used to calculate the Floater position. If it's not set, it will use the `children` as the target.

**title** {node}  
It can be anything that can be rendered.

**wrapperOptions** {object}  
Position the wrapper relative to the target.  
*You need to set a `target` for this to work.*

```js
{
    offset: number, // The distance between the wrapper and the target. It can be negative.
    placement: string, // the same options as above, except center
    position: bool, // Set to true to position the wrapper
}
```


## Styling
You can customize everything with the `styles` prop.  
Only set the properties you want to change and the default [styles](./src/styles.js) will be merged.

Check it [styles.js](./src/styles.js) for the syntax.


## Modes

**Default**  
The wrapper will trigger the events and use itself as the Floater's target.

```jsx
<Floater content="This is the Floater content">
    <span>click me</span>
</Floater>

```

**Proxy**  
The wrapper will trigger the events but the Floater will use the **target** prop to position itself.

```jsx
<div className="App">
    <img src="some-path" />

    <Floater
      content="This is the Floater content"
      target=".App img"
    >
        <span>click me</span>
    </Floater>
</div>

```

**Beacon**  
The same as the **proxy mode** but the wrapper will be positioned relative to the `target`.

```jsx
<div className="App">
    <img src="https://upload.wikimedia.org/wikipedia/commons/2/2d/Google-favicon-2015.png" width="100" className="my-super-image" />

    <Floater
        content="This is the Floater content"
        target=".my-super-image"
        wrapperOptions={{
            offset: -22,
            placement: 'top',
            position: true,
        }}
    >
    <span style={{ color: '#f04', fontSize: 34 }}>◉</span>
    </Floater>
</div>

```

**Controlled**  
When you set a boolean to the `open` prop it will enter the controlled mode and it will not respond to events.  
In this mode you don't even need to have `children`

```jsx
<div className="App">
    <img src="some-path" />
    <Floater
        content="This is the Floater content"
        open={true}
        target=".App img"
    />
</div>
```