├── .babelrc ├── .gitignore ├── History.md ├── LICENSE ├── Makefile ├── Readme.md ├── build └── index.js ├── example ├── index.html ├── index.js └── style.css ├── index.js └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react", "stage-0"], 3 | "plugins": [], 4 | "env": { 5 | "development": { 6 | "plugins": [ 7 | ["react-transform", { 8 | "transforms": [{ 9 | "transform": "react-transform-hmr", 10 | "imports": ["react"], 11 | "locals": ["module"] 12 | }] 13 | }] 14 | ] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | example/bundle.js 2 | node_modules 3 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | v1.1.1 / 2017-05-02 2 | =================== 3 | 4 | * use `prop-types` module 5 | 6 | 7 | v1.1.0 / 2016-02-24 8 | =================== 9 | 10 | * add PropTypes 11 | * add example 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2016 TJ Holowaychuk tj@tjholowaychuk.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | include node_modules/react-fatigue-dev/Makefile 3 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # ClickOutside 3 | 4 | React click outside component. 5 | 6 | ## Installation 7 | 8 | ``` 9 | $ npm install tj/react-click-outside 10 | ``` 11 | 12 | ## Example 13 | 14 | ```js 15 | 16 |

Im a menu or something that you want to hide when clicking outside.

17 |
18 | ``` 19 | 20 | ## Badges 21 | 22 | ![](https://img.shields.io/badge/license-MIT-blue.svg) 23 | ![](https://img.shields.io/badge/status-stable-green.svg) 24 | 25 | --- 26 | 27 | > [tjholowaychuk.com](http://tjholowaychuk.com)  ·  28 | > GitHub [@tj](https://github.com/tj)  ·  29 | > Twitter [@tjholowaychuk](https://twitter.com/tjholowaychuk) 30 | -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 8 | 9 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 10 | 11 | var _react = require('react'); 12 | 13 | var _react2 = _interopRequireDefault(_react); 14 | 15 | var _propTypes = require('prop-types'); 16 | 17 | var _propTypes2 = _interopRequireDefault(_propTypes); 18 | 19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 20 | 21 | function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } 22 | 23 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 24 | 25 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 26 | 27 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 28 | 29 | var ClickOutside = function (_Component) { 30 | _inherits(ClickOutside, _Component); 31 | 32 | function ClickOutside(props) { 33 | _classCallCheck(this, ClickOutside); 34 | 35 | var _this = _possibleConstructorReturn(this, (ClickOutside.__proto__ || Object.getPrototypeOf(ClickOutside)).call(this, props)); 36 | 37 | _this.handle = function (e) { 38 | if (e.type === 'touchend') _this.isTouch = true; 39 | if (e.type === 'click' && _this.isTouch) return; 40 | var onClickOutside = _this.props.onClickOutside; 41 | 42 | var el = _this.container; 43 | if (el && !el.contains(e.target)) onClickOutside(e); 44 | }; 45 | 46 | _this.getContainer = _this.getContainer.bind(_this); 47 | _this.isTouch = false; 48 | return _this; 49 | } 50 | 51 | _createClass(ClickOutside, [{ 52 | key: 'getContainer', 53 | value: function getContainer(ref) { 54 | this.container = ref; 55 | } 56 | }, { 57 | key: 'render', 58 | value: function render() { 59 | var _props = this.props, 60 | children = _props.children, 61 | onClickOutside = _props.onClickOutside, 62 | props = _objectWithoutProperties(_props, ['children', 'onClickOutside']); 63 | 64 | return _react2.default.createElement( 65 | 'div', 66 | _extends({}, props, { ref: this.getContainer }), 67 | children 68 | ); 69 | } 70 | }, { 71 | key: 'componentDidMount', 72 | value: function componentDidMount() { 73 | document.addEventListener('touchend', this.handle, true); 74 | document.addEventListener('click', this.handle, true); 75 | } 76 | }, { 77 | key: 'componentWillUnmount', 78 | value: function componentWillUnmount() { 79 | document.removeEventListener('touchend', this.handle, true); 80 | document.removeEventListener('click', this.handle, true); 81 | } 82 | }]); 83 | 84 | return ClickOutside; 85 | }(_react.Component); 86 | 87 | ClickOutside.propTypes = { 88 | onClickOutside: _propTypes2.default.func.isRequired 89 | }; 90 | exports.default = ClickOutside; -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react' 3 | import ReactDOM from 'react-dom' 4 | import ClickOutside from '../index' 5 | 6 | class Menu extends Component { 7 | state = { 8 | open: false 9 | }; 10 | 11 | render() { 12 | const { open } = this.state 13 | 14 | const items = 21 | 22 | return
23 | 24 | Menu 25 | {open ? items : null} 26 | 27 |
28 | } 29 | 30 | toggle() { 31 | const { open } = this.state 32 | this.setState({ open: !open }) 33 | } 34 | 35 | hide() { 36 | this.setState({ open: false }) 37 | } 38 | } 39 | 40 | ReactDOM.render(, document.querySelector('#content')) 41 | -------------------------------------------------------------------------------- /example/style.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | padding: 50px; 4 | font: 15px/1.6 Helvetica, Arial, sans-serif; 5 | } 6 | 7 | .Menu ul { 8 | border: 1px solid #eee; 9 | } 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | export default class ClickOutside extends Component { 5 | static propTypes = { 6 | onClickOutside: PropTypes.func.isRequired 7 | } 8 | 9 | constructor(props) { 10 | super(props) 11 | this.getContainer = this.getContainer.bind(this) 12 | this.isTouch = false 13 | } 14 | 15 | getContainer(ref) { 16 | this.container = ref 17 | } 18 | 19 | render() { 20 | const { children, onClickOutside, ...props } = this.props 21 | return
{children}
22 | } 23 | 24 | componentDidMount() { 25 | document.addEventListener('touchend', this.handle, true) 26 | document.addEventListener('click', this.handle, true) 27 | } 28 | 29 | componentWillUnmount() { 30 | document.removeEventListener('touchend', this.handle, true) 31 | document.removeEventListener('click', this.handle, true) 32 | } 33 | 34 | handle = e => { 35 | if (e.type === 'touchend') this.isTouch = true 36 | if (e.type === 'click' && this.isTouch) return 37 | const { onClickOutside } = this.props 38 | const el = this.container 39 | if (el && !el.contains(e.target)) onClickOutside(e) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-click-outside", 3 | "version": "1.1.1", 4 | "description": "ClickOutside component for React", 5 | "keywords": [ 6 | "react", 7 | "click", 8 | "outside", 9 | "component" 10 | ], 11 | "main": "build/index.js", 12 | "author": "TJ Holowaychuk ", 13 | "license": "MIT", 14 | "browserify": { 15 | "transform": [ 16 | "babelify" 17 | ] 18 | }, 19 | "devDependencies": { 20 | "prop-types": "^15.5.8", 21 | "react-fatigue-dev": "github:tj/react-fatigue-dev#704f778" 22 | } 23 | } 24 | --------------------------------------------------------------------------------