├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── dependency-review.yml │ └── npm-publish-github-packages.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── package-lock.json ├── package.json ├── src ├── ModalContext.js ├── ModalProvider.jsx ├── index.js └── useModal.js └── webpack.config.js /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 4 | # 5 | # Source repository: https://github.com/actions/dependency-review-action 6 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 7 | name: 'Dependency Review' 8 | on: [pull_request] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | dependency-review: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: 'Checkout Repository' 18 | uses: actions/checkout@v3 19 | - name: 'Dependency Review' 20 | uses: actions/dependency-review-action@v2 21 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish-github-packages.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | - run: npm ci 19 | - run: npm run build 20 | 21 | publish-gpr: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: read 26 | packages: write 27 | steps: 28 | - uses: actions/checkout@v3 29 | - name: Use Node.js 30 | uses: actions/setup-node@v3 31 | with: 32 | always-auth: true 33 | node-version: "16.x" 34 | registry-url: https://registry.npmjs.org 35 | scope: "@alexander.khivrych" 36 | - run: npm ci 37 | - run: npm publish 38 | env: 39 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # builds 8 | build 9 | dist 10 | .rpt2_cache 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | .npmrc -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | alexander.khivrych@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First, thanks for being willing to contribute! 4 | 5 | ## Project Setup 6 | 7 | 1. Fork and clone this repo 8 | 1. Run `yarn install` to install all dependencies (_we require yarn instead of any other JS package manager_) 9 | 3. Create a branch for your PR with `git checkout -b your-branch-name` 10 | 11 | > ℹ️ Credit: From [React Testing Library][react-testing-lib]\ 12 | > Tip: Keep your `main` branch pointing at the original repository and make pull 13 | > requests from branches on your fork. To do this, run: 14 | > 15 | > ``` 16 | > git remote add upstream https://github.com/alexanderkhivrych/use-modal-hook 17 | > git fetch upstream 18 | > git branch --set-upstream-to=upstream/main main 19 | > ``` 20 | > 21 | > This will add the original repository as a "remote" called "upstream", then 22 | > fetch the git information from that remote and set your local `main` branch 23 | > to use the upstream main branch whenever you run `git pull`. You can now make 24 | > all of your pull request branches based on this `main` branch. Whenever you 25 | > want to update your version of `main`, do a regular `git pull`. 26 | 27 | ## Standards 28 | 29 | - Commit messages follow the [Conventional Commit spec][conventional-commit]; 30 | - Code styling is enforced via ESLint and Prettier. 31 | 32 | Our CI setup, using Github Actions, will also check these standards before contributions can be merged. 33 | 34 | ## Help needed 35 | 36 | Please checkout the [the open issues][issues]. 37 | 38 | 39 | [issues]: https://github.com/alexanderkhivrych/use-modal-hook/issues 40 | [conventional-commit]: https://www.conventionalcommits.org/en/v1.0.0/ 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Oleksandr (Sasha) Khivrych 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 |
2 |

3 |
4 | use-modal-hook ❤️ 5 |
6 |

7 | 8 |
9 |
10 | 11 | PRs welcome 12 | 13 | 14 | npm package 15 | 16 | 17 | npm downloads 18 | 19 | 20 | Maintenance 21 | 22 |
23 |
24 | 25 | > React hook for controlling modal components 26 | 27 | ## Install 28 | 29 | ```bash 30 | #With npm 31 | npm install use-modal-hook --save 32 | ``` 33 | 34 | ```bash 35 | #With yarn 36 | yarn add use-modal-hook 37 | ``` 38 | 39 | ## Usage 40 | [![Edit react use modal hook example](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/2zz9w1pwrr?fontsize=14) 41 | ```jsx 42 | import React, { memo } from "react"; 43 | import { useModal, ModalProvider } from "use-modal-hook"; 44 | import Modal from "react-modal"; // It can be any modal 45 | 46 | const MyModal = memo( 47 | ({ isOpen, onClose, title, description, closeBtnLabel }) => ( 48 | 49 |

{title}

50 |
{description}
51 | 52 |
53 | ) 54 | ); 55 | 56 | const SomePage = memo(() => { 57 | const [showModal, hideModal] = useModal(MyModal, { 58 | title: "My Test Modal", 59 | description: "I Like React Hooks", 60 | closeBtnLabel: "Close" 61 | }); 62 | 63 | return ( 64 | <> 65 |

Test Page

66 | 67 | 68 | ); 69 | }); 70 | 71 | const App = () => ( 72 | 73 | 74 | 75 | ); 76 | 77 | ``` 78 | 79 | #### `useModal(, , ): [showModal: Function, hideModal: Function]` 80 | Param | Type | Description 81 | --- | --- | --- 82 | ModalComponent | `Function` | It can be any [`react`](https://reactjs.org/docs/react-api.html) component that you want to use for show modal 83 | modalProps | `Object` | Props that you want to pass to your modal component 84 | showModal | `Function` | It is function for show your modal, you can pass any dynamic props to this function 85 | hideModal | `Function` | It is function for hide your modal, you can pass any dynamic props to this function 86 | onClose | `Function` | It callback will be triggered after modal window closes 87 | 88 | #### `showModal(dynamicModalProps: Object)` 89 | Param | Type | Description 90 | --- | --- | --- 91 | dynamicModalProps | `Object` | Dynamic props that you want to pass to your modal component 92 | 93 | ## License 94 | 95 | MIT © [alexanderkhivrych](https://github.com/alexanderkhivrych) 96 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "use-modal-hook", 3 | "version": "2.1.2", 4 | "description": "React hook for controlling modal components", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "./node_modules/.bin/webpack --mode=production" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/alexanderkhivrych/use-modal-hook.git" 12 | }, 13 | "keywords": [ 14 | "react", 15 | "use-modal", 16 | "react-modal-hook", 17 | "hook", 18 | "react-hooks", 19 | "modal", 20 | "modal-hook", 21 | "use-modal-hook", 22 | "useModal" 23 | ], 24 | "dependencies": { 25 | "prop-types": "^15.8.1", 26 | "react": "^18.2.0 ", 27 | "react-dom": "^18.2.0 " 28 | }, 29 | "peerDependencies": { 30 | "prop-types": "^15.8.1", 31 | "react": "^18.2.0", 32 | "react-dom": "^18.2.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "^7.13.16", 36 | "@babel/preset-env": "^7.13.15", 37 | "@babel/preset-react": "^7.13.13", 38 | "babel-loader": "^8.2.2", 39 | "parcel-bundler": "^1.6.2", 40 | "webpack": "^5.74.0", 41 | "webpack-cli": "^4.10.0", 42 | "webpack-node-externals": "^3.0.0" 43 | }, 44 | "files": [ 45 | "dist" 46 | ], 47 | "author": "alexander.khivrych", 48 | "license": "MIT", 49 | "bugs": { 50 | "url": "https://github.com/alexanderkhivrych/use-modal-hook/issues" 51 | }, 52 | "homepage": "https://github.com/alexanderkhivrych/use-modal-hook#readme" 53 | } 54 | -------------------------------------------------------------------------------- /src/ModalContext.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ModalContext = React.createContext({ 4 | showModal: null, 5 | hideModal: null, 6 | isOpenedModal: false, 7 | }); 8 | 9 | export default ModalContext; 10 | -------------------------------------------------------------------------------- /src/ModalProvider.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useMemo, useCallback } from 'react'; 2 | import ModalContext from './ModalContext'; 3 | 4 | const ModalProvider = ({ children }) => { 5 | const [modalsConfig, setConfig] = useState({}); 6 | const hideModal = useCallback( 7 | (modalKey, onClose) => { 8 | setConfig(prevConfig => ({ ...prevConfig, [modalKey]: { ...prevConfig[modalKey], isOpen: false } })); 9 | 10 | if (onClose) { 11 | onClose(); 12 | } 13 | }, 14 | [setConfig] 15 | ); 16 | const showModal = useCallback( 17 | (modalKey, component, modalData) => { 18 | setConfig(prevConfig => ({ ...prevConfig, [modalKey]: { isOpen: true, component, data: modalData } })); 19 | }, 20 | [setConfig] 21 | ); 22 | const contextValue = useMemo( 23 | () => ({ 24 | showModal, 25 | hideModal, 26 | isOpenedModal: Object.values(modalsConfig).some(({ isOpen }) => isOpen), 27 | }), 28 | [hideModal, showModal, modalsConfig] 29 | ); 30 | 31 | return ( 32 | 33 | {children} 34 | {Object.keys(modalsConfig).map(modalKey => { 35 | const { component: Component, isOpen, data } = modalsConfig[modalKey]; 36 | 37 | return isOpen && hideModal(modalKey)} key={modalKey} isOpen={isOpen} {...data} />; 38 | })} 39 | 40 | ); 41 | }; 42 | 43 | export default ModalProvider; 44 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as ModalContext } from "./ModalContext"; 2 | export { default as ModalProvider } from "./ModalProvider"; 3 | export { default as useModal } from "./useModal"; -------------------------------------------------------------------------------- /src/useModal.js: -------------------------------------------------------------------------------- 1 | import { useContext, useMemo, useCallback } from 'react'; 2 | import ModalContext from './ModalContext'; 3 | import ModalProvider from './ModalProvider'; 4 | 5 | const generateModalKey = (() => { 6 | let count = 0; 7 | 8 | return () => `${++count}`; 9 | })(); 10 | 11 | function useModal(component, data, onClose) { 12 | const key = useMemo(generateModalKey, []); 13 | const context = useContext(ModalContext); 14 | const showModal = useCallback( 15 | modalData => context.showModal(key, component, modalData instanceof Event ? data : { ...data, ...modalData }), 16 | [data, context.showModal] 17 | ); 18 | const hideModal = useCallback(() => context.hideModal(key, onClose), [context.hideModal, onClose, key]); 19 | 20 | return [showModal, hideModal]; 21 | } 22 | 23 | useModal.ModalContext = ModalContext; 24 | useModal.ModalProvider = ModalProvider; 25 | 26 | export default useModal; 27 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const nodeExternals = require('webpack-node-externals'); 3 | 4 | module.exports = { 5 | entry: path.resolve(__dirname, 'src/index.js'), 6 | output: { 7 | path: path.resolve(__dirname, './dist'), 8 | filename: 'index.js', 9 | libraryTarget: 'commonjs' 10 | }, 11 | externals: [nodeExternals()], 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.jsx?$/, 16 | exclude: /(node_modules)/, 17 | loader: 'babel-loader', 18 | options: { 19 | presets: ['@babel/preset-env', '@babel/react'] 20 | } 21 | }, 22 | ] 23 | }, 24 | resolve: { 25 | extensions: ['.js', '.jsx'], 26 | } 27 | }; --------------------------------------------------------------------------------