Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-portal

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-portal - npm Package Compare versions

Comparing version 0.7.0 to 1.0.0

29

examples/absoluteposition.js

@@ -1,14 +0,7 @@

var React = require('react');
import React from 'react';
var AbsolutePosition = React.createClass({
export default class AbsolutePosition extends React.Component {
propTypes: {
top: React.PropTypes.number,
left: React.PropTypes.number,
width: React.PropTypes.number,
closePortal: React.PropTypes.func
},
render: function() {
var style = {
render() {
const style = {
position: 'absolute',

@@ -26,3 +19,6 @@ top: this.props.top,

<div style={style}>
<p>This portal is opened manually and given an absolute position using: the opening element's <i>onClick</i> prop, and the portal's <i>isOpened</i> prop.</p>
<p>
This portal is opened manually and given an absolute position using:{' '}
the opening element's <i>onClick</i> prop, and the portal's <i>isOpened</i> prop.
</p>
<p>Click anywhere outside to close it.</p>

@@ -33,4 +29,9 @@ </div>

});
}
module.exports = AbsolutePosition;
AbsolutePosition.propTypes = {
top: React.PropTypes.number,
left: React.PropTypes.number,
width: React.PropTypes.number,
closePortal: React.PropTypes.func
};

@@ -1,35 +0,38 @@

var React = require('react');
var Portal = require('../lib/portal.js');
var PseudoModal = require('./pseudomodal');
var LoadingBar = require('./loadingbar');
var AbsolutePosition = require('./absoluteposition');
import React from 'react';
import ReactDOM from 'react-dom';
import Portal from '../lib/portal.js';
import PseudoModal from './pseudomodal';
import LoadingBar from './loadingbar';
import AbsolutePosition from './absoluteposition';
// Main React app component
var App = React.createClass({
export default class App extends React.Component {
getInitialState: function() {
return {
constructor(props) {
super(props);
this.state = {
isPortalOpened: false,
someValue: 'init'
};
},
}
onClose: function() {
onClose() {
/*eslint no-console: 0*/
console.log('Portal closed');
},
}
render: function() {
var button1 = <button>Open portal with pseudo modal</button>;
var button2 = <button>Another portal</button>;
var button3 = (
<button onClick={(function(e) {
var bodyRect = document.body.getBoundingClientRect();
var targetRect = e.target.getBoundingClientRect();
this.setState({
isOpened: true,
top: targetRect.top - bodyRect.top,
left: targetRect.left - bodyRect.left,
width: targetRect.width
});
}).bind(this)}>
render() {
const button1 = <button>Open portal with pseudo modal</button>;
const button2 = <button>Another portal</button>;
const button3 = (
<button onClick={(e) => {
const bodyRect = document.body.getBoundingClientRect();
const targetRect = e.target.getBoundingClientRect();
this.setState({
isOpened: true,
top: targetRect.top - bodyRect.top,
left: targetRect.left - bodyRect.left,
width: targetRect.width
});
}}>
{'Open portal on top of button'}

@@ -45,3 +48,3 @@ </button>

<Portal closeOnEsc={true} openByClickOn={button1} testProp={this.state.someValue}>
<Portal closeOnEsc openByClickOn={button1} testProp={this.state.someValue}>
<PseudoModal>

@@ -56,3 +59,3 @@ <h2>Pseudo Modal</h2>

<button onClick={this.toggleLoadingBar}>
<button onClick={(e) => this.toggleLoadingBar(e)}>
{this.state.isPortalOpened ? 'Close the second portal' : 'Open the second portal'}

@@ -64,3 +67,3 @@ </button>

<Portal closeOnOutsideClick={true} openByClickOn={button2} onClose={this.onClose}>
<Portal closeOnOutsideClick onClose={this.onClose} openByClickOn={button2}>
<div style={{border: '1px solid black', margin: 10, padding: 10}}>

@@ -74,13 +77,11 @@ <p>Click anywhere outside of this portal to close it.</p>

<Portal
closeOnOutsideClick={true}
closeOnOutsideClick
isOpened={this.state.isOpened}
onClose={(function() {
this.setState({isOpened: false});
this.onClose();
}).bind(this)}>
<AbsolutePosition top={this.state.top} left={this.state.left} width={this.state.width} />
onClose={() => {this.setState({isOpened: false}); this.onClose();}}
>
<AbsolutePosition left={this.state.left} top={this.state.top} width={this.state.width} />
</Portal>
</div>
<button onClick={this.changeValue}>
<button onClick={(e) => this.changeValue(e)}>
Change randomly value: {this.state.someValue}

@@ -91,14 +92,14 @@ </button>

);
},
}
toggleLoadingBar: function(e) {
toggleLoadingBar(e) {
this.setState({isPortalOpened: !this.state.isPortalOpened});
},
}
changeValue: function(e) {
changeValue(e) {
this.setState({someValue: Math.random().toString(36).substring(7)});
}
});
}
React.render(React.createElement(App), document.getElementById('react-body'));
ReactDOM.render(<App />, document.getElementById('react-body'));

@@ -1,11 +0,6 @@

var React = require('react');
import React from 'react';
var LoadingBar = React.createClass({
export default class LoadingBar extends React.Component {
propTypes: {
children: React.PropTypes.element,
closePortal: React.PropTypes.func
},
render: function() {
render() {
return (

@@ -21,4 +16,7 @@ <div style={{border: '1px solid green', margin: 10, padding: 10}}>

});
}
module.exports = LoadingBar;
LoadingBar.propTypes = {
children: React.PropTypes.element,
closePortal: React.PropTypes.func
};

@@ -1,14 +0,6 @@

var React = require('react');
import React from 'react';
var PseudoModal = React.createClass({
export default class PseudoModal extends React.Component {
propTypes: {
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]),
closePortal: React.PropTypes.func
},
render: function() {
render() {
return (

@@ -22,4 +14,10 @@ <div style={{border: '1px solid blue', margin: 10, padding: 10}}>

});
}
module.exports = PseudoModal;
PseudoModal.propTypes = {
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]),
closePortal: React.PropTypes.func
};

@@ -1,4 +0,5 @@

import React, {findDOMNode} from 'react';
import React from 'react';
import ReactDOM, {findDOMNode} from 'react-dom';
import CSSPropertyOperations from 'react/lib/CSSPropertyOperations';
import shallowEqual from 'react/lib/shallowEqual';
import shallowCompare from 'react/lib/shallowCompare';

@@ -70,4 +71,3 @@ export default class Portal extends React.Component {

shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState);
return shallowCompare(this, nextProps, nextState);
}

@@ -86,3 +86,3 @@

}
this.portal = React.render(React.cloneElement(props.children, {closePortal: this.closePortal}), this.node);
this.portal = ReactDOM.render(React.cloneElement(props.children, {closePortal: this.closePortal}), this.node);
}

@@ -109,3 +109,3 @@

if (this.node) {
React.unmountComponentAtNode(this.node);
ReactDOM.unmountComponentAtNode(this.node);
document.body.removeChild(this.node);

@@ -149,3 +149,3 @@ }

function isNodeInRoot(node, root) {
export function isNodeInRoot(node, root) {
while (node) {

@@ -152,0 +152,0 @@ if (node === root) {

@@ -5,4 +5,4 @@ {

"description": "Simple React component for transportation of your modals, lightboxes, etc. to document.body",
"author": "Vojtech Miksu",
"version": "0.7.0",
"author": "Vojtech Miksu <vojtech@miksu.cz>",
"version": "1.0.0",
"license": "MIT",

@@ -26,18 +26,23 @@ "repository": {

],
"engines": {
"node": "^4.4.1",
"npm": "^3.3.6"
},
"peerDependencies": {
"react": "^0.13.x"
"react": "^0.14.0",
"react-dom": "^0.14.0"
},
"devDependencies": {
"babel-eslint": "^3.0.1",
"babelify": "^6.0.2",
"browserify": "^9.0.8",
"eslint-plugin-react": "^2.1.0",
"gulp": "^3.8.11",
"gulp-babel": "^5.1.0",
"babel-eslint": "^4.1.3",
"babelify": "^6.3.0",
"browserify": "^11.2.0",
"eslint-plugin-react": "^3.5.1",
"gulp": "^3.9.0",
"gulp-babel": "^5.2.1",
"gulp-connect": "^2.2.0",
"gulp-eslint": "^0.9.0",
"gulp-livereload": "^3.8.0",
"gulp-eslint": "^1.0.0",
"gulp-livereload": "^3.8.1",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.1.1"
"watchify": "^3.4.0"
}
}
'use strict';
var _interopRequireWildcard = function (obj) { return obj && obj.__esModule ? obj : { 'default': obj }; };
Object.defineProperty(exports, '__esModule', {
value: true
});
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } };
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; }; })();
var _get = function get(object, property, receiver) { var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
var _inherits = function (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) subClass.__proto__ = superClass; };
exports.isNodeInRoot = isNodeInRoot;
Object.defineProperty(exports, '__esModule', {
value: true
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _React$findDOMNode = require('react');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _React$findDOMNode2 = _interopRequireWildcard(_React$findDOMNode);
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; }
var _CSSPropertyOperations = require('react/lib/CSSPropertyOperations');
var _react = require('react');
var _CSSPropertyOperations2 = _interopRequireWildcard(_CSSPropertyOperations);
var _react2 = _interopRequireDefault(_react);
var _shallowEqual = require('react/lib/shallowEqual');
var _reactDom = require('react-dom');
var _shallowEqual2 = _interopRequireWildcard(_shallowEqual);
var _reactDom2 = _interopRequireDefault(_reactDom);
var _reactLibCSSPropertyOperations = require('react/lib/CSSPropertyOperations');
var _reactLibCSSPropertyOperations2 = _interopRequireDefault(_reactLibCSSPropertyOperations);
var _reactLibShallowCompare = require('react/lib/shallowCompare');
var _reactLibShallowCompare2 = _interopRequireDefault(_reactLibShallowCompare);
var Portal = (function (_React$Component) {
_inherits(Portal, _React$Component);
function Portal() {

@@ -43,4 +51,2 @@ _classCallCheck(this, Portal);

_inherits(Portal, _React$Component);
_createClass(Portal, [{

@@ -104,3 +110,3 @@ key: 'componentDidMount',

value: function shouldComponentUpdate(nextProps, nextState) {
return !_shallowEqual2['default'](this.props, nextProps) || !_shallowEqual2['default'](this.state, nextState);
return (0, _reactLibShallowCompare2['default'])(this, nextProps, nextState);
}

@@ -116,7 +122,7 @@ }, {

if (props.style) {
_CSSPropertyOperations2['default'].setValueForStyles(this.node, props.style);
_reactLibCSSPropertyOperations2['default'].setValueForStyles(this.node, props.style);
}
document.body.appendChild(this.node);
}
this.portal = _React$findDOMNode2['default'].render(_React$findDOMNode2['default'].cloneElement(props.children, { closePortal: this.closePortal }), this.node);
this.portal = _reactDom2['default'].render(_react2['default'].cloneElement(props.children, { closePortal: this.closePortal }), this.node);
}

@@ -127,3 +133,3 @@ }, {

if (this.props.openByClickOn) {
return _React$findDOMNode2['default'].createElement(
return _react2['default'].createElement(
'div',

@@ -151,3 +157,3 @@ { className: 'openByClickOn', onClick: this.openPortal.bind(this, this.props) },

if (this.node) {
_React$findDOMNode2['default'].unmountComponentAtNode(this.node);
_reactDom2['default'].unmountComponentAtNode(this.node);
document.body.removeChild(this.node);

@@ -169,3 +175,3 @@ }

}
if (isNodeInRoot(e.target, _React$findDOMNode.findDOMNode(this.portal))) {
if (isNodeInRoot(e.target, (0, _reactDom.findDOMNode)(this.portal))) {
return;

@@ -187,3 +193,3 @@ }

return Portal;
})(_React$findDOMNode2['default'].Component);
})(_react2['default'].Component);

@@ -193,10 +199,10 @@ exports['default'] = Portal;

Portal.propTypes = {
className: _React$findDOMNode2['default'].PropTypes.string,
style: _React$findDOMNode2['default'].PropTypes.object,
children: _React$findDOMNode2['default'].PropTypes.element.isRequired,
openByClickOn: _React$findDOMNode2['default'].PropTypes.element,
closeOnEsc: _React$findDOMNode2['default'].PropTypes.bool,
closeOnOutsideClick: _React$findDOMNode2['default'].PropTypes.bool,
isOpened: _React$findDOMNode2['default'].PropTypes.bool,
onClose: _React$findDOMNode2['default'].PropTypes.func
className: _react2['default'].PropTypes.string,
style: _react2['default'].PropTypes.object,
children: _react2['default'].PropTypes.element.isRequired,
openByClickOn: _react2['default'].PropTypes.element,
closeOnEsc: _react2['default'].PropTypes.bool,
closeOnOutsideClick: _react2['default'].PropTypes.bool,
isOpened: _react2['default'].PropTypes.bool,
onClose: _react2['default'].PropTypes.func
};

@@ -212,3 +218,2 @@

return false;
}
module.exports = exports['default'];
}

@@ -8,23 +8,32 @@ React-portal

> Struggling with modals, lightboxes or loading bars in React? Look no further. This is the component that will help you.
> Struggling with modals, lightboxes or loading bars in React? Look no further. React-portal creates a new top-level React tree and injects its child into it.
## Features
- transports its child into a new React component and appends it to the **document.body**
- transports its child into a new React component and appends it to the **document.body** (creates a new independent React tree)
- can be opened by the prop **isOpened**
- can be opened after click on an element that you pass through the prop **openByClickOn** (and then it takes care of the state)
- doesn't leave any DOM mess after closing
- can be opened after a click on an element that you pass through the prop **openByClickOn** (and then it takes care of the open/close state)
- doesn't leave any mess in DOM after closing
- provides its child with **this.props.closePortal** callback
- provides **close on ESC** and **close on outside mouse click** out of the box (see the docs)
- provides **close on ESC** and **close on outside mouse click** out of the box
- supports absolute positioned components (great for tooltips)
## Demo
Try [http://miksu.cz/react-portal](http://miksu.cz/react-portal)
Try [http://miksu.cz/react-portal](http://miksu.cz/react-portal) **or**
Or `git clone http://github.com/tajo/react-portal` and open `/examples/index.html`
```shell
git clone https://github.com/tajo/react-portal
```
and open
```
/examples/index.html
```
## Installation
```shell
npm install react-portal --save
npm install react react-dom react-portal --save
```

@@ -34,12 +43,13 @@

```javascript
var React = require('react');
var Portal = require('react-portal');
import React from 'react';
import ReactDOM from 'react-dom';
import Portal from 'react-portal';
var App = React.createClass({
export default class App extends React.Component {
render: function() {
var button1 = <button>Open portal with pseudo modal</button>;
render() {
const button1 = <button>Open portal with pseudo modal</button>;
return (
<Portal openByClickOn={button1} closeOnEsc={true} closeOnOutsideClick={true}>
<Portal closeOnEsc closeOnOutsideClick openByClickOn={button1}>
<PseudoModal>

@@ -53,7 +63,7 @@ <h2>Pseudo Modal</h2>

});
}
var PseudoModal = React.createClass({
export class PseudoModal extends React.Component {
render: function() {
render() {
return (

@@ -67,5 +77,5 @@ <div>

});
}
React.render(React.createElement(App), document.getElementById('react-body'));
ReactDOM.render(<App />, document.getElementById('react-body'));
```

@@ -75,13 +85,10 @@ ## Documentation - props

#### children : ReactElement (required)
The Portal expects one child (`<Portal><Child ... /></Portal>`) that will be ported.
The portal expects one child (`<Portal><Child ... /></Portal>`) that will be ported.
#### isOpened : bool (optional)
If true, the portal is open. If false, the portal is closed. It's up to you to take
care of the closing (aka taking care of the state).
If true, the portal is open. If false, the portal is closed. It's up to you to take care of the closing (aka taking care of the state). Don't use this prop if you want to make your life easier. Use openByClickOn instead!
#### openByClickOn : ReactElement (optional)
The second way how to open the portal. This element will be rendered by the portal immediately
with `onClick = open portal`. How to close the portal then? It provides its child with
the callback `this.props.closePortal`. Or you can use built-in portal closing (see bellow).
Notice that you don't have to deal with the state (like when using the `isOpened` prop).
with `onClick` handler that triggers portal opening. **How to close the portal then?** The portal provides its ported child with a callback `this.props.closePortal`. Or you can use built-in portal closing methods (closeOnEsc, ... more below). Notice that you don't have to deal with the open/close state (like when using the `isOpened` prop).

@@ -99,8 +106,25 @@ #### closeOnEsc: bool (optional)

- Does your modal have a fullscreen overlay and the `closeOnOutsideClick` doesn't work? [There is a simple solution](https://github.com/tajo/react-portal/issues/2#issuecomment-92058826).
- Does your inner inner component `<Portal><LevelOne><LevelTwo /></LevelOne></Portal>` also need an access to `this.props.closePortal()`? You can't just use `{this.props.children}` in `<LevelOne>` component. You need to clone it instead: `{React.cloneElement(this.props.children, {closePortal: this.props.closePortal})}`.
- Does your inner inner component `<LevelTwo />`
```js
<Portal>
<LevelOne>
<LevelTwo />
</LevelOne>
</Portal>
```
also needs an access to `this.props.closePortal()`? You can't just use `{this.props.children}` in render method of `<LevelOne>` component. You have to clone it instead:
```js
{React.cloneElement(
this.props.children,
{closePortal: this.props.closePortal}
)}
```
#### Don't read this
Please, skip this section if you dislike dirty tricks.
States make everything harder, right? We don't want to deal with them, right? But sometime you need to open a portal (e.g. modal) automatically. There is no button to click on. No problem, because the portal has the `isOpen` prop, so you can just set it `true` or `false`.
**States make everything harder, right?** We don't want to deal with them, right? But sometime you need to open a portal (e.g. modal) automatically. There is no button to click on. No problem, because the portal has the `isOpen` prop, so you can just set it `true` or `false`.

@@ -118,3 +142,4 @@ However, then it's completely up to you to take care about the open state. You have to write all the closing logic! And that sucks. But there is a dirty trick:

```javascript
findDOMNode(this.refs.myLittleSecret).click(); // opens the portal, yay!
ReactDOM.findDOMNode(this.refs.myLittleSecret).click();
// opens the portal, yay!
```

@@ -132,3 +157,3 @@

npm install
npm install react
npm install react react-dom
gulp

@@ -138,3 +163,3 @@ ```

- Don't commit the main build `portal.js` (aka don't run `npm run build`)
- Run `gulp eslint` before every commit to preserve the coding style. Do you know there is a [nice real-time checking integration for your editor](http://eslint.org/docs/user-guide/integrations)? ;-)
- **Run `gulp eslint` before every commit** to preserve the coding style. Do you know there is a [nice real-time checking integration for your editor](http://eslint.org/docs/user-guide/integrations)?

@@ -141,0 +166,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc