diff --git a/README.md b/README.md index 64106f3..e74007d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ npm install react-onclickoutside --save (or `--save-dev` depending on your needs). You then use it in your components as: -``` +```javascript var Component = React.createClass({ mixins: [ require('react-onclickoutside') @@ -39,13 +39,13 @@ bower install react-onclickoutside and then include it as script via: -``` +```html ``` Then use it as: -``` +```javascript var Component = React.createClass({ mixins: [ OnClickOutside @@ -66,7 +66,7 @@ When using this mixin, a component has two functions that can be used to explici In addition, you can create a component that uses this mixin such that it has the code set up and ready to go, but not listening for outside click events until you explicitly issue its `enableOnClickOutside()`, by passing in a properly called `disableOnClickOutside`: -``` +```javascript var Component = React.createClass({ mixins: [ ... ], handleClickOutside: function(evt) { @@ -85,4 +85,41 @@ var Container = React.createClass({ If you want the mixin to ignore certain elements, then add the class `ignore-react-onclickoutside` to that element and the callback won't be invoked when the click happens inside elements with that class. +## ES6/2015 class support via HOC / ES7 decorators + +Since mixins can't be used with ES6/2015 class React components a +[Higher-Order Component (HOC)](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750) +and [ES7 decorator](https://github.com/wycats/javascript-decorators) are bundled with the mixin: + +```javascript +import listensToClickOutside from 'react-onclickoutside/decorator'; + +class Component extends React.Component { + handleClickOutside = (event) => { + // ... + } +} + +export default listensToClickOutside(Component); + +// OR + +import listensToClickOutside from 'react-onclickoutside/decorator'; + +@listensToClickOutside() +class Component extends React.Component { + handleClickOutside = (event) => { + // ... + } +} + +export default Component; +``` + +One difference when using the HOC/decorator compared to the mixin is that the `enableOnClickOutside()` +and `disableOnClickOutside()` methods are not available as class methods, but rather on the `props`; +so instead of `this.enableOnClickOutside()` you would call `this.props.enableOnClickOutside()`. + +In every other respect the the mixin and HOC/decorator provides the same functionality. + For bugs and enhancements hit up https://github.com/Pomax/react-onclickoutside/issues diff --git a/decorator.js b/decorator.js new file mode 100644 index 0000000..dcd2052 --- /dev/null +++ b/decorator.js @@ -0,0 +1,97 @@ +var React = require('react'); +var objectAssign = require('react/lib/Object.assign'); +var OnClickOutsideMixin = require('react-onclickoutside'); + + +function addClickOutsideListener(Component) { + + return React.createClass({ + + displayName: (Component.displayName || Component.name) + 'ClickOutside', + + mixins: [OnClickOutsideMixin], + + handleClickOutside: function(event) { + if (this.refs.inner.handleClickOutside) { + this.refs.inner.handleClickOutside(event); + } + else if (this.props.onClickOutside) { + this.props.onClickOutside(event); + } + }, + + render: function render() { + return React.createElement(Component, objectAssign({ + enableOnClickOutside: this.enableOnClickOutside, + disableOnClickOutside: this.disableOnClickOutside, + ref: 'inner' + }, this.props)); + } + }); +} + + +/** + * @function listensToClickOutside + * + * A higher-order component for ES6 React classes to use the `handleClickOutside` event handler: + * + * import listensToClickOutside from 'react-onclickoutside/decorator'; + * + * class Es6Component extends React.Component { + * handleClickOutside = (event) => { + * // ...handling code goes here... + * } + * } + * + * export default listensToClickOutside(Es6Component); + * + * Alternatively you can pass the handler down from the parent on an `onClickOutside` prop: + * + * class Child extends React.Component { + * // No event handler here, if provided this handler takes precedence and the one passed down + * // is not called automatically. If it should be, call it on the props from the child handler. + * } + * + * Child = listenToClickOutside(Child); + * + * + * class Parent extends React.Component { + * handleClickOutside = (event) => { + * // ...handling code goes here... + * } + * + * render() { + * return ( + * + * ); + * } + * } + * + * The [ES7 Decorator Pattern](https://github.com/wycats/javascript-decorators) is also supported + * using the same import: + * + * import listensToClickOutside from 'react-onclickoutside/decorator'; + * + * @listensToClickOutside() + * class Es6Component extends React.Component { + * handleClickOutside = (event) => { + * // ...handling code goes here... + * } + * } + * + * @param {React.Component} [Component] The component outside of which to listen to clicks. + * @returns {React.Component} or {Function} if using the decorator pattern. + */ +function listensToClickOutside(Component) { + // support decorator pattern + if (!Component) { + return function listensToClickOutsideDecorator(ComponentToDecorate) { + return addClickOutsideListener(ComponentToDecorate); + }; + } + + return addClickOutsideListener(Component); +} + +module.exports = listensToClickOutside;