react-relative-portal
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -7,2 +7,4 @@ 'use strict'; | ||
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; }; | ||
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; }; }(); | ||
@@ -42,11 +44,37 @@ | ||
_this.handleOutClick = function (e) { | ||
if (typeof _this.props.onOutClick === 'function') { | ||
var root = _reactDom2.default.findDOMNode(_this.element); | ||
if (!root.contains(e.target)) { | ||
_this.props.onOutClick(); | ||
_this.root = null; | ||
_this.handleRootRef = function (root) { | ||
if (root !== _this.root) { | ||
if (_this.root) { | ||
_this.root.removeEventListener('click', _this.handleInClick); | ||
} | ||
if (root) { | ||
root.addEventListener('click', _this.handleInClick); | ||
} | ||
} | ||
_this.root = root; | ||
}; | ||
// The previous implementation triggered `onOutClick` after a click in the `Portal` content | ||
// if it gets re-rendered during that click. It assumed that if the clicked element | ||
// is not found in the root element via `root.contains(e.target)`, it's outside. | ||
// But if after re-render the clicked element gets removed from the DOM, so it cannot be found | ||
// in the root element. Instead we capture and flag the click event before it bubbles up | ||
// to the `document` to be handled by `handleOutClick`. | ||
_this.isInClick = false; | ||
_this.handleInClick = function () { | ||
_this.isInClick = true; | ||
}; | ||
_this.handleOutClick = function () { | ||
var isOutClick = !_this.isInClick; | ||
_this.isInClick = false; | ||
var onOutClick = _this.props.onOutClick; | ||
if (isOutClick && typeof onOutClick === 'function') { | ||
onOutClick(); | ||
} | ||
}; | ||
document.addEventListener('click', _this.handleOutClick); | ||
@@ -64,4 +92,5 @@ } | ||
// eslint-disable-line | ||
this.element = _reactDom2.default.unstable_renderSubtreeIntoContainer(this, _react2.default.createElement('div', props), this.node); | ||
// eslint-disable-line no-unused-vars | ||
// It's recommended to use `ref` callbacks instead of `findDOMNode`. https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md | ||
_reactDom2.default.unstable_renderSubtreeIntoContainer(this, _react2.default.createElement('div', _extends({}, props, { ref: this.handleRootRef })), this.node); | ||
} | ||
@@ -72,2 +101,6 @@ }, { | ||
if (_exenv.canUseDOM) { | ||
// `this.handleRootRef` won't be called with `null`, so cleanup here. | ||
if (this.root) { | ||
this.root.removeEventListener('click', this.handleInClick); | ||
} | ||
document.removeEventListener('click', this.handleOutClick); | ||
@@ -78,9 +111,2 @@ document.body.removeChild(this.node); | ||
}, { | ||
key: 'handleOutClick', | ||
value: function handleOutClick() { | ||
if (this.props.onOutClick) { | ||
this.props.onOutClick(); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
@@ -87,0 +113,0 @@ value: function render() { |
{ | ||
"name": "react-relative-portal", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "React component for place dropdowns outside overflow: hidden; elements", | ||
@@ -5,0 +5,0 @@ "main": "dist/RelativePortal.js", |
@@ -5,3 +5,3 @@ # React relative portal | ||
## Installation | ||
`npm instal react-relative-portal --save` | ||
`npm install react-relative-portal --save` | ||
@@ -22,10 +22,32 @@ ## Example | ||
this.handleToggle = () => { | ||
this.setState({ show: !this.state.show }); | ||
this._setShowAsyncTimer = null; | ||
this._handleShow = () => { | ||
this._setShowAsync(true); | ||
}; | ||
this.handleHide = () => { | ||
this.setState({ show: false }); | ||
this._handleHide = () => { | ||
this._setShowAsync(false); | ||
}; | ||
} | ||
componentWillUnmount() { | ||
// Prevent the asynchronous `setState` call after unmount. | ||
clearTimeout(this._setShowAsyncTimer); | ||
} | ||
/** | ||
* Changes the dropdown show/hide state asynchronously. | ||
* | ||
* Need to change the dropdown state asynchronously, | ||
* otherwise the dropdown gets immediately closed | ||
* during the dropdown toggle's `onClick` which propagates to `onOutClick`. | ||
*/ | ||
_setShowAsync(show) { | ||
// Prevent multiple asynchronous `setState` calls, jsut the latest has to happen. | ||
clearTimeout(this._setShowAsyncTimer); | ||
this._setShowAsyncTimer = setTimeout(() => { | ||
this.setState({ show: show }); | ||
}, 0); | ||
} | ||
@@ -37,3 +59,3 @@ render() { | ||
<div> | ||
<button onClick={this.handleToggle}> | ||
<button onClick={show ? this._handleHide : this._handleShow}> | ||
Dropdown toggle | ||
@@ -45,3 +67,3 @@ </button> | ||
top={10} | ||
onOutClick={this.handleHide} | ||
onOutClick={show ? this._handleHide : null} | ||
> | ||
@@ -48,0 +70,0 @@ {show && |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
16750
229
102