diff --git a/README.md b/README.md
index 22df069..2b8e2e8 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ const PopperExample = () => (
)
```
-## Usage w/ child function
+## Usage with child function
This is a useful way to interact with custom components. Just make sure you pass down the refs properly.
@@ -77,6 +77,49 @@ const PopperExample = () => (
)
```
+## Usage without Manager
+
+It's generally easiest to let the `Manager` and `Target` components handle passing the target DOM element to the `Popper` component. However, you can pass a target [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or a [referenceObject](https://popper.js.org/popper-documentation.html#referenceObject) directly into `Popper` if you need to.
+
+Handling DOM Elements from React can be complicated. The `Manager` and `Target` components handle these complexities for you, so their use is strongly recommended when using DOM Elements.
+
+```js
+import { PureComonent } from 'react'
+import { Popper, Arrow } from 'react-popper'
+
+class StandaloneExample extends PureComponent {
+ state = {
+ isOpen: false,
+ }
+
+ handleClick() = () => {
+ this.setState(prevState => ({
+ isOpen: !prevState.isOpen
+ }))
+ }
+
+ render() {
+ return (
+
+
this.target = div}
+ style={{ width: 120, height: 120, background: '#b4da55' }}
+ onClick={this.handleClick}
+ >
+ Click {this.state.isOpen ? 'to hide' : 'to show'} popper
+
+ {this.state.isOpen && (
+
+ Popper Content
+
+
+ )}
+
+ )
+ }
+}
+```
+
## `Shared Props`
`Target`, `Popper`, and `Arrow` all share the following props
@@ -124,11 +167,12 @@ A `Target`'s child may be one of the following:
Your popper that gets attached to the `Target` component.
-Each `Popper` must be wrapped in a `Manager`, and each `Manager` can wrap multiple `Popper` components.
+Each `Popper` must either be wrapped in a `Manager`, or passed a `target` prop directly. Each `Manager` can wrap multiple `Popper` components.
#### `placement`: PropTypes.oneOf(Popper.placements)
#### `eventsEnabled`: PropTypes.bool
#### `modifiers`: PropTypes.object
+#### `target`: PropTypes.oneOfType([PropTypes.instanceOf(Element), Popper.referenceObject])
Passes respective options to a new [Popper instance](https://github.com/FezVrasta/popper.js/blob/master/docs/_includes/popper-documentation.md#new-popperreference-popper-options). As for `onCreate` and `onUpdate`, these callbacks were intentionally left out in favor of using the [component lifecycle methods](https://facebook.github.io/react/docs/react-component.html#the-component-lifecycle). If you have a good use case for these please feel free to file and issue and I will consider adding them in.
diff --git a/example/index.jsx b/example/index.jsx
index 1e27988..f14e5a5 100644
--- a/example/index.jsx
+++ b/example/index.jsx
@@ -4,6 +4,9 @@ import ReactDOM from 'react-dom'
import MultipleExample from './multiple'
import AnimatedExample from './animated'
import ModifiersExample from './modifiers'
+import ToggleableExample from './toggleable'
+import StandaloneExample from './standalone'
+import StandaloneObjectExample from './standaloneObject'
import './main.css'
@@ -18,6 +21,15 @@ const App = () => (
+
+
+
+
+
+
+
+
+
)
diff --git a/example/standalone.jsx b/example/standalone.jsx
new file mode 100644
index 0000000..57c9277
--- /dev/null
+++ b/example/standalone.jsx
@@ -0,0 +1,37 @@
+import React, { PureComponent } from 'react'
+import { Popper, Arrow } from '../src/react-popper'
+
+class StandaloneExample extends PureComponent {
+ state = {
+ isOpen: false,
+ }
+
+ handleClick = () => {
+ this.setState(prevState => ({
+ isOpen: !prevState.isOpen,
+ }))
+ }
+
+ render() {
+ return (
+
+
Standalone Popper Example
+
(this.target = div)}
+ style={{ width: 120, height: 120, background: '#b4da55' }}
+ onClick={this.handleClick}
+ >
+ Click {this.state.isOpen ? 'to hide' : 'to show'} popper
+
+ {this.state.isOpen && (
+
+ Popper Content for Standalone example
+
+
+ )}
+
+ )
+ }
+}
+
+export default StandaloneExample
diff --git a/example/standaloneObject.jsx b/example/standaloneObject.jsx
new file mode 100644
index 0000000..8b7fe6d
--- /dev/null
+++ b/example/standaloneObject.jsx
@@ -0,0 +1,48 @@
+import React, { PureComponent } from 'react'
+import { Popper, Arrow } from '../src/react-popper'
+
+class StandaloneObjectExample extends PureComponent {
+ state = {
+ isOpen: false,
+ }
+
+ handleClick = () => {
+ this.setState(prevState => ({
+ isOpen: !prevState.isOpen,
+ }))
+ }
+
+ render() {
+ const reference = {
+ getBoundingClientRect: () => ({
+ top: 10,
+ left: 100,
+ right: 150,
+ bottom: 90,
+ width: 50,
+ height: 80,
+ }),
+ clientWidth: 50,
+ clientHeight: 80,
+ }
+ return (
+
+
Standalone referenceObject Popper Example
+
+ Click {this.state.isOpen ? 'to hide' : 'to show'} popper
+
+ {this.state.isOpen && (
+
+ Popper Content for Standalone example
+
+
+ )}
+
+ )
+ }
+}
+
+export default StandaloneObjectExample
diff --git a/example/toggleable.jsx b/example/toggleable.jsx
new file mode 100644
index 0000000..bf1f706
--- /dev/null
+++ b/example/toggleable.jsx
@@ -0,0 +1,38 @@
+import React, { PureComponent } from 'react'
+import { Manager, Target, Popper, Arrow } from '../src/react-popper'
+
+class ToggleableExample extends PureComponent {
+ state = {
+ isOpen: false,
+ }
+
+ handleClick = () => {
+ this.setState(prevState => ({
+ isOpen: !prevState.isOpen,
+ }))
+ }
+
+ render() {
+ return (
+
+
Toggleable Popper Example
+
+
+ Click {this.state.isOpen ? 'to hide' : 'to show'} popper
+
+ {this.state.isOpen && (
+
+ Popper Content for Toggleable Example
+
+
+ )}
+
+
+ )
+ }
+}
+
+export default ToggleableExample
diff --git a/src/Popper.jsx b/src/Popper.jsx
index df803e3..f7cde22 100644
--- a/src/Popper.jsx
+++ b/src/Popper.jsx
@@ -4,7 +4,7 @@ import PopperJS from 'popper.js'
class Popper extends Component {
static contextTypes = {
- popperManager: PropTypes.object.isRequired,
+ popperManager: PropTypes.object,
}
static childContextTypes = {
@@ -18,6 +18,14 @@ class Popper extends Component {
eventsEnabled: PropTypes.bool,
modifiers: PropTypes.object,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
+ target: PropTypes.oneOfType([
+ PropTypes.instanceOf(Element),
+ PropTypes.shape({
+ getBoundingClientRect: PropTypes.func.isRequired,
+ clientWidth: PropTypes.number.isRequired,
+ clientHeight: PropTypes.number.isRequired,
+ }),
+ ]),
}
static defaultProps = {
@@ -41,7 +49,8 @@ class Popper extends Component {
componentDidUpdate(lastProps) {
if (
lastProps.placement !== this.props.placement ||
- lastProps.eventsEnabled !== this.props.eventsEnabled
+ lastProps.eventsEnabled !== this.props.eventsEnabled ||
+ lastProps.target !== this.props.target
) {
this._destroyPopper()
this._createPopper()
@@ -60,6 +69,16 @@ class Popper extends Component {
}
_getTargetNode = () => {
+ if (this.props.target) {
+ return this.props.target
+ } else if (
+ !this.context.popperManager ||
+ !this.context.popperManager.getTargetNode()
+ ) {
+ throw new Error(
+ 'Target missing. Popper must be given a target from the Popper Manager, or as a prop.',
+ )
+ }
return this.context.popperManager.getTargetNode()
}