├── .babelrc ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── compositor.json ├── example ├── .babelrc ├── index.html ├── package.json ├── src │ ├── app.js │ ├── exampleModal.js │ ├── index.js │ └── reducer.js └── webpack.config.js ├── package.json └── src ├── action.js ├── baseModalProvider.js ├── constants.js ├── eventEmitter.js ├── events.js ├── index.js ├── reducer.js ├── singleModalProvider.js └── stackableModalProvider.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "prettier", 4 | "prettier/react" 5 | ], 6 | "parser": "babel-eslint", 7 | "env": { 8 | "browser": true 9 | }, 10 | "rules": { 11 | "react/jsx-filename-extension": "off" 12 | }, 13 | "plugins": [ 14 | "prettier" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | 4 | lib 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Maxim Yaskevich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Redux Modal Provider 2 | 3 | `react-redux-modal-provider` controls the state of your React modal components using Redux. 4 | 5 | ## Installation 6 | ``` 7 | npm i --save react-redux-modal-provider 8 | ``` 9 | 10 | ## Usage 11 | 12 | #### 1. Add `` to your root component. 13 | 14 | ```jsx 15 | import ModalProvider from 'react-redux-modal-provider'; 16 | 17 | export default render( 18 | 19 |
20 | 21 | 22 |
23 |
, 24 | document.getElementById('app') 25 | ); 26 | ``` 27 | 28 | #### 2. Plug in Modal Provider reducer. 29 | 30 | ```jsx 31 | import { reducer as modalProvider } from 'react-redux-modal-provider'; 32 | 33 | export default combineReducers({ 34 | modalProvider, 35 | }); 36 | ``` 37 | 38 | #### 3. Add modal creation code. 39 | 40 | ```jsx 41 | // app.jsx 42 | import { showModal } from 'react-redux-modal-provider'; 43 | import MyModal from './myModal'; 44 | 45 | export default (props) => ( 46 |
47 |

48 | Hello world 49 |

50 | 55 |
56 | ); 57 | ``` 58 | 59 | #### 4. Handle modal closing. 60 | 61 | ```jsx 62 | // myModal.jsx 63 | import { Modal } from 'react-bootstrap'; 64 | 65 | export default (props) => ( 66 | 67 | 68 | {props.message} 69 | 70 | 71 | 72 | 73 | 74 | 75 | ); 76 | ``` 77 | 78 | `show` and `hideModal` props are passed in automatically. 79 | 80 | ## Implementations 81 | 82 | ### `StackableModalProvider` (default) 83 | 84 | This is the default `ModalProvider` implementation. Each new modal stacks up on top of previous one. 85 | 86 | ```jsx 87 | import { StackableModalProvider } from 'react-redux-modal-provider'; 88 | 89 | export default render( 90 | 91 |
92 | 93 | 94 |
95 |
, 96 | document.getElementById('app') 97 | ); 98 | ``` 99 | 100 | ### `SingleModalProvider` 101 | 102 | One modal at a time. Each new modal triggers `hideModal` on previous one. 103 | 104 | ```jsx 105 | import { SingleModalProvider } from 'react-redux-modal-provider'; 106 | 107 | export default render( 108 | 109 |
110 | 111 | 112 |
113 |
, 114 | document.getElementById('app') 115 | ); 116 | ``` 117 | 118 | ## How is it different from [`redux-modal`](https://github.com/yesmeck/redux-modal)? 119 | 120 | 1. You don't have to think about where your modal component should fit into component tree, because it doesn't really matter where to render a modal. 121 | 122 | 2. No need to `connect()` your modal component to Redux, unless you want it to be able to create other modals itself. 123 | 124 | ## Acknowledgements 125 | 126 | * Thanks [@yesmeck](https://github.com/yesmeck), author of [`redux-modal`](https://github.com/yesmeck/redux-modal), for webpack config I borrowed. 127 | 128 | * Thanks [@diegoddox](https://github.com/diegoddox), author of [`react-redux-toastr`](https://github.com/diegoddox/react-redux-toastr), for the idea how to dispatch actions from anywhere using `EventEmitter`. 129 | 130 | ## License 131 | MIT 132 | -------------------------------------------------------------------------------- /compositor.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myaskevich/react-redux-modal-provider", 3 | "version": "0.1.4", 4 | "libraries": { 5 | "xv": "^1.1.25" 6 | }, 7 | "title": "asdf", 8 | "branch": "", 9 | "style": { 10 | "name": "Swiss", 11 | "componentSet": { 12 | "nav": "nav/BasicNav", 13 | "header": "header/SwissHeader", 14 | "article": "article/SwissArticle", 15 | "footer": "footer/BasicFooter" 16 | }, 17 | "fontFamily": "\"Helvetica Neue\", Helvetica, Arial, sans-serif", 18 | "heading": { 19 | "fontWeight": 700, 20 | "letterSpacing": "-0.01em" 21 | }, 22 | "layout": { 23 | "fluid": true 24 | }, 25 | "colors": { 26 | "text": "#000", 27 | "background": "#fff", 28 | "primary": "#05a", 29 | "secondary": "#444", 30 | "highlight": "#f30", 31 | "border": "#ccc", 32 | "muted": "#eee" 33 | } 34 | }, 35 | "content": [ 36 | { 37 | "component": "nav", 38 | "links": [ 39 | { 40 | "href": "https://github.com/myaskevich/react-redux-modal-provider", 41 | "text": "GitHub" 42 | }, 43 | { 44 | "href": "https://npmjs.com/package/react-redux-modal-provider", 45 | "text": "npm" 46 | } 47 | ] 48 | }, 49 | { 50 | "component": "header", 51 | "heading": "react-redux-modal-provider", 52 | "subhead": "Redux-powered abstract React modal state manager", 53 | "children": [ 54 | { 55 | "component": "ui/TweetButton", 56 | "text": "react-redux-modal-provider: Redux-powered abstract React modal state manager", 57 | "url": null 58 | }, 59 | { 60 | "component": "ui/GithubButton", 61 | "user": "myaskevich", 62 | "repo": "react-redux-modal-provider" 63 | } 64 | ], 65 | "text": "v2.0.1" 66 | }, 67 | { 68 | "component": "article", 69 | "metadata": { 70 | "source": "github.readme" 71 | }, 72 | "html": "

React Redux Modal Provider

\n

react-redux-modal-provider controls the state of your React modal components using Redux.

\n

Installation

\n
npm i --save react-redux-modal-provider

What's new in v2

\n

No need to use connect() anymore!

\n

Before:

\n
// app.jsx\nimport { connect } from 'react-redux';\nimport { showModal } from 'react-redux-modal-provider';\nimport MyModal from './myModal';\n\nconst App = (props) => (\n  <div>\n    <p>\n      Hello world\n    </p>\n    <button\n      type="button"\n      onClick={() => props.showModal(MyModal, { message: 'Hello' })}>\n      Present modal\n    </button>\n  </div>\n);\n\nexport default connect(null, { showModal })(App);

After:

\n
// app.jsx\nimport { showModal } from 'react-redux-modal-provider';\nimport MyModal from './myModal';\n\nexport default (props) => (\n  <div>\n    <p>\n      Hello world\n    </p>\n    <button\n      type="button"\n      onClick={() => showModal(MyModal, { message: 'Hello' })}>\n      Present modal\n    </button>\n  </div>\n);

Usage

\n

1. Add <ModalProvider> to your root component.

\n
import ModalProvider from 'react-redux-modal-provider';\n\nexport default render(\n  <Provider store={store}>\n    <div>\n      <App />\n      <ModalProvider />\n    </div>\n  </Provider>,\n  document.getElementById('app')\n);

2. Plug in Modal Provider reducer.

\n
import { reducer as modalProvider } from 'react-redux-modal-provider';\n\nexport default combineReducers({\n  modalProvider,\n});

3. Add modal creation code.

\n
// app.jsx\nimport { showModal } from 'react-redux-modal-provider';\nimport MyModal from './myModal';\n\nexport default (props) => (\n  <div>\n    <p>\n      Hello world\n    </p>\n    <button\n      type="button"\n      onClick={() => showModal(MyModal, { message: 'Hello' })}>\n      Present modal\n    </button>\n  </div>\n);

4. Handle modal closing.

\n
// myModal.jsx\nimport { Modal } from 'react-bootstrap';\n\nexport default (props) => (\n  <Modal show={props.show}>\n    <Modal.Body>\n      {props.message}\n    </Modal.Body>\n\n    <Modal.Footer>\n      <Button onClick={props.hideModal}>Ok</Button>\n    </Modal.Footer>\n  </Modal>\n);

show and hideModal props are passed in automatically.

\n

Implementations

\n

StackableModalProvider (default)

\n

This is the default ModalProvider implementation. Each new modal stacks up on top of previous one.

\n
import { StackableModalProvider } from 'react-redux-modal-provider';\n\nexport default render(\n  <Provider store={store}>\n    <div>\n      <App />\n      <StackableModalProvider />\n    </div>\n  </Provider>,\n  document.getElementById('app')\n);

SingleModalProvider

\n

One modal at a time. Each new modal triggers hideModal on previous one.

\n
import { SingleModalProvider } from 'react-redux-modal-provider';\n\nexport default render(\n  <Provider store={store}>\n    <div>\n      <App />\n      <SingleModalProvider />\n    </div>\n  </Provider>,\n  document.getElementById('app')\n);

How is it different from redux-modal?

\n
    \n
  1. You don't have to think about where your modal component should fit into component tree, because it doesn't really matter where to render a modal.

    \n
  2. \n
  3. No need to connect() your modal component to Redux, unless you want it to be able to create other modals itself.

    \n
  4. \n
\n

Acknowledgements

\n\n

License

\n

MIT

\n" 73 | }, 74 | { 75 | "component": "footer", 76 | "links": [ 77 | { 78 | "href": "https://github.com/myaskevich/react-redux-modal-provider", 79 | "text": "GitHub" 80 | }, 81 | { 82 | "href": "https://github.com/myaskevich", 83 | "text": "myaskevich" 84 | } 85 | ] 86 | } 87 | ] 88 | } -------------------------------------------------------------------------------- /example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --colors --content-base . --hot --inline" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "react": "^15.4.2", 14 | "react-bootstrap": "^0.30.7", 15 | "react-dom": "^15.4.2", 16 | "react-redux": "^5.0.3", 17 | "redux": "^3.6.0" 18 | }, 19 | "devDependencies": { 20 | "webpack": "^2.2.1", 21 | "webpack-dev-server": "^2.4.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/src/app.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { showModal } from 'react-redux-modal-provider'; 3 | 4 | import ExampleModal from './exampleModal'; 5 | 6 | class App extends Component { 7 | state = { 8 | message: 'Pass me to the modal' 9 | } 10 | 11 | render() { 12 | return ( 13 |
14 |

15 | React Modal Provider App 16 |

17 |

18 | this.setState({message: e.target.value})} 21 | /> 22 |

23 |

24 | 32 |

33 |
34 | ); 35 | } 36 | } 37 | 38 | export default App; 39 | -------------------------------------------------------------------------------- /example/src/exampleModal.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { Button, Modal } from 'react-bootstrap'; 3 | import { showModal } from 'react-redux-modal-provider'; 4 | 5 | const ExampleModal = props => ( 6 | 7 | 8 | Hello {props.index} 9 | 10 | 11 | 12 | {props.message} 13 | 14 | 15 | 16 | 17 | 26 | 27 | 28 | ); 29 | 30 | ExampleModal.propTypes = { 31 | show: PropTypes.bool.isRequired, 32 | hideModal: PropTypes.func.isRequired, 33 | index: PropTypes.number, 34 | message: PropTypes.string.isRequired, 35 | } 36 | 37 | ExampleModal.defaultProps = { 38 | index: 1, 39 | }; 40 | 41 | export default ExampleModal; 42 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { createStore } from 'redux'; 5 | import ModalProvider from 'react-redux-modal-provider'; 6 | 7 | import rootReducer from './reducer'; 8 | 9 | import App from './app'; 10 | 11 | const store = createStore(rootReducer); 12 | 13 | export default render( 14 | 15 |
16 | 17 | 18 |
19 |
, 20 | document.getElementById('app'), 21 | ); 22 | -------------------------------------------------------------------------------- /example/src/reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { reducer as modalProvider } from 'react-redux-modal-provider'; 3 | 4 | export default combineReducers({ 5 | modalProvider, 6 | }); 7 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const reactReduxModalProviderSrc = path.resolve(__dirname, '../src') 4 | 5 | module.exports = { 6 | entry: './src/index.js', 7 | output: { 8 | path: __dirname, 9 | filename: 'bundle.js' 10 | }, 11 | module: { 12 | loaders: [ 13 | { 14 | test: /\.js$/, 15 | exclude: /node_modules/, 16 | include: [ __dirname, reactReduxModalProviderSrc ], 17 | loader: 'babel-loader' 18 | } 19 | ] 20 | }, 21 | resolve: { 22 | alias: { 'react-redux-modal-provider': reactReduxModalProviderSrc } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-redux-modal-provider", 3 | "version": "2.2.0", 4 | "description": "Redux-powered abstract React modal state manager", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "lint": "esw src example/src --fix", 8 | "build": "babel src --out-dir lib", 9 | "prepublish": "npm run build", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "files": [ 13 | "lib", 14 | "src" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/myaskevich/react-redux-modal-provider.git" 19 | }, 20 | "keywords": [ 21 | "react", 22 | "redux", 23 | "modal", 24 | "provider" 25 | ], 26 | "author": "Maxim Yaskevich ", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/myaskevich/react-redux-modal-provider/issues" 30 | }, 31 | "homepage": "https://github.com/myaskevich/react-redux-modal-provider#readme", 32 | "peerDependencies": { 33 | "react": "^0.14.0 || ^15.0.0", 34 | "react-redux": "^4.0.0 || ^5.0.0", 35 | "redux": "^3.0.0" 36 | }, 37 | "devDependencies": { 38 | "babel-cli": "^6.23.0", 39 | "babel-core": "^6.23.1", 40 | "babel-eslint": "^7.1.1", 41 | "babel-loader": "^6.3.2", 42 | "babel-preset-es2015": "^6.22.0", 43 | "babel-preset-react": "^6.23.0", 44 | "babel-preset-stage-0": "^6.22.0", 45 | "eslint": "^3.17.0", 46 | "eslint-config-prettier": "^1.5.0", 47 | "eslint-plugin-import": "^2.2.0", 48 | "eslint-plugin-jsx-a11y": "^4.0.0", 49 | "eslint-plugin-prettier": "^2.0.1", 50 | "eslint-plugin-react": "^6.10.0", 51 | "eslint-watch": "^3.0.0", 52 | "prettier": "^0.21.0", 53 | "prop-types": "^15.0.0", 54 | "react": "^15.4.2", 55 | "react-redux": "^5.0.3", 56 | "redux": "^3.6.0" 57 | }, 58 | "dependencies": { 59 | "eventemitter3": "^2.0.3" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/action.js: -------------------------------------------------------------------------------- 1 | import { DELAY_BEFORE_REMOVE_MSEC } from './constants'; 2 | 3 | export const ActionType = { 4 | SHOW: '@react-redux-modal-provider.show', 5 | HIDE: '@react-redux-modal-provider.hide', 6 | REMOVE: '@react-redux-modal-provider.remove', 7 | }; 8 | 9 | export const showModal = (component, props = {}, delayBeforeRemoveMsec = DELAY_BEFORE_REMOVE_MSEC) => { 10 | if (!component) { 11 | return undefined; 12 | } 13 | 14 | return { 15 | type: ActionType.SHOW, 16 | component, 17 | props, 18 | delayBeforeRemoveMsec, 19 | }; 20 | }; 21 | 22 | export const hideModal = id => ({ 23 | type: ActionType.HIDE, 24 | id, 25 | }); 26 | 27 | export const removeModal = id => ({ 28 | type: ActionType.REMOVE, 29 | id, 30 | }); 31 | -------------------------------------------------------------------------------- /src/baseModalProvider.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import eventEmitter from './eventEmitter'; 5 | import { DELAY_BEFORE_REMOVE_MSEC } from './constants'; 6 | 7 | export default class BaseModalProvider extends Component { 8 | componentWillMount() { 9 | eventEmitter.on('showModal', (args) => this.props.showModal(...args)); 10 | } 11 | 12 | componentWillUnmount() { 13 | eventEmitter.removeListener('showModal'); 14 | } 15 | 16 | hideModal(id, delayBeforeRemoveMsec = DELAY_BEFORE_REMOVE_MSEC) { 17 | this.props.hideModal(id); 18 | setTimeout( 19 | () => { 20 | this.props.removeModal(id); 21 | }, 22 | delayBeforeRemoveMsec, 23 | ); 24 | } 25 | 26 | render() { 27 | return ( 28 |
29 | {this.props.modalProvider.stack.map(modal => ( 30 | this.hideModal(modal.id, modal.delayBeforeRemoveMsec)} 34 | show={modal.show} 35 | /> 36 | ))} 37 |
38 | ); 39 | } 40 | } 41 | 42 | BaseModalProvider.propTypes = { 43 | showModal: PropTypes.func.isRequired, 44 | hideModal: PropTypes.func.isRequired, 45 | removeModal: PropTypes.func.isRequired, 46 | modalProvider: PropTypes.shape({ 47 | stack: PropTypes.arrayOf( 48 | PropTypes.shape({ 49 | id: PropTypes.number.isRequired, 50 | component: PropTypes.oneOfType([ 51 | PropTypes.element, 52 | PropTypes.func 53 | ]).isRequired, 54 | props: PropTypes.object, 55 | show: PropTypes.bool.isRequired, 56 | }), 57 | ), 58 | }).isRequired, 59 | }; 60 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | export const DELAY_BEFORE_REMOVE_MSEC = 1000; -------------------------------------------------------------------------------- /src/eventEmitter.js: -------------------------------------------------------------------------------- 1 | 2 | import EventEmitter from 'eventemitter3'; 3 | 4 | export default new EventEmitter(); 5 | -------------------------------------------------------------------------------- /src/events.js: -------------------------------------------------------------------------------- 1 | 2 | import eventEmitter from './eventEmitter'; 3 | 4 | export default (...args) => eventEmitter.emit('showModal', args); 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export default from './stackableModalProvider'; 2 | export StackableModalProvider from './stackableModalProvider'; 3 | export SingleModalProvider from './singleModalProvider'; 4 | 5 | export showModal from './events'; 6 | 7 | export reducer from './reducer'; 8 | -------------------------------------------------------------------------------- /src/reducer.js: -------------------------------------------------------------------------------- 1 | import { ActionType } from './action'; 2 | 3 | const reducer = ( 4 | state = { 5 | stack: [], 6 | }, 7 | action, 8 | ) => { 9 | switch (action.type) { 10 | case ActionType.SHOW: 11 | return { 12 | ...state, 13 | stack: [ 14 | ...state.stack, 15 | { 16 | id: new Date().valueOf(), 17 | component: action.component, 18 | props: action.props, 19 | show: true, 20 | delayBeforeRemoveMsec: action.delayBeforeRemoveMsec, 21 | }, 22 | ], 23 | }; 24 | 25 | case ActionType.HIDE: 26 | const targetModal = state.stack.find(modal => modal.id === action.id) 27 | 28 | if (!targetModal) { 29 | return state; 30 | } 31 | 32 | return { 33 | ...state, 34 | stack: [ 35 | ...state.stack.filter(modal => state.stack.indexOf(modal) < state.stack.indexOf(targetModal)), 36 | { 37 | ...targetModal, 38 | show: false, 39 | }, 40 | ...state.stack.filter(modal => state.stack.indexOf(modal) > state.stack.indexOf(targetModal)), 41 | ], 42 | }; 43 | 44 | case ActionType.REMOVE: 45 | return { 46 | ...state, 47 | stack: state.stack.filter(modal => modal.id !== action.id), 48 | }; 49 | 50 | default: 51 | return state; 52 | } 53 | }; 54 | 55 | export default reducer; 56 | -------------------------------------------------------------------------------- /src/singleModalProvider.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import { showModal, hideModal, removeModal } from './action'; 4 | import BaseModalProviderComponent from './baseModalProvider'; 5 | 6 | class SingleModalProvider extends BaseModalProviderComponent { 7 | componentWillReceiveProps(nextProps) { 8 | const previousStackSize = this.props.modalProvider.stack.length; 9 | const nextStackSize = nextProps.modalProvider.stack.length; 10 | 11 | if (nextStackSize > previousStackSize) { 12 | if (nextStackSize > 1) { 13 | this.hideModal(nextProps.modalProvider.stack[nextStackSize - 2].id); 14 | } 15 | } 16 | } 17 | } 18 | 19 | export default connect( 20 | ({ modalProvider }) => ({ 21 | modalProvider, 22 | }), 23 | { 24 | showModal, 25 | hideModal, 26 | removeModal, 27 | }, 28 | )(SingleModalProvider); 29 | -------------------------------------------------------------------------------- /src/stackableModalProvider.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import { showModal, hideModal, removeModal } from './action'; 4 | import BaseModalProviderComponent from './baseModalProvider'; 5 | 6 | class StackableModalProvider extends BaseModalProviderComponent { 7 | } 8 | 9 | export default connect( 10 | ({ modalProvider }) => ({ 11 | modalProvider, 12 | }), 13 | { 14 | showModal, 15 | hideModal, 16 | removeModal, 17 | }, 18 | )(StackableModalProvider); 19 | --------------------------------------------------------------------------------