├── .babelrc ├── .gitignore ├── jsconfig.json ├── src └── index.js ├── lib └── index.js ├── component ├── Component.vue └── Component.spec.js ├── .github └── workflows │ └── npm-publish.yml ├── package.json └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env"] 4 | ] 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export const clickOutSide = { 2 | mounted: function (el, binding) { 3 | el.clickOutsideEvent = function (event) { 4 | if (!(el == event.target || el.contains(event.target))) { 5 | binding.value(event, el) 6 | } 7 | } 8 | document.addEventListener("click", el.clickOutsideEvent) 9 | }, 10 | unmounted: function (el) { 11 | document.removeEventListener("click", el.clickOutsideEvent) 12 | }, 13 | } 14 | 15 | export default clickOutSide 16 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var clickOutSide = exports.clickOutSide = { 7 | mounted: function mounted(el, binding, vnode) { 8 | el.clickOutsideEvent = function (event) { 9 | if (!(el == event.target || el.contains(event.target))) { 10 | binding.value(event, el); 11 | } 12 | }; 13 | document.addEventListener("click", el.clickOutsideEvent); 14 | }, 15 | unmounted: function unmounted(el) { 16 | document.removeEventListener("click", el.clickOutsideEvent); 17 | } 18 | }; 19 | 20 | exports.default = clickOutSide; -------------------------------------------------------------------------------- /component/Component.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | BOX {{ counter }} 10 | 11 | 12 | Outside of the box 13 | 14 | 15 | 16 | 37 | 38 | 53 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.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: Vue.js Library 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 test 20 | 21 | publish-npm: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: actions/setup-node@v3 27 | with: 28 | node-version: 16 29 | registry-url: https://registry.npmjs.org/ 30 | - run: npm ci 31 | - run: npm publish --access=public 32 | env: 33 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 34 | -------------------------------------------------------------------------------- /component/Component.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | import { mount } from '@vue/test-utils' 6 | 7 | import clickOutSide from "../lib"; 8 | 9 | import Box from './Component.vue' 10 | 11 | test('box element is not visible at first', () => { 12 | const wrapper = mount(Box) 13 | 14 | expect(wrapper.find('.box').exists()).toBe(true) 15 | }) 16 | 17 | test('box counter increases if button is clicked', async () => { 18 | const wrapper = mount(Box) 19 | 20 | expect(wrapper.find('.box').exists()).toBe(true) 21 | 22 | const button = wrapper.find('.box') 23 | await button.trigger('click') 24 | 25 | expect(wrapper.find('.box').text()).toBe('BOX 1') 26 | }) 27 | 28 | // TODO: fix it. 29 | test('box element is invisible if data visible is false', async () => { 30 | const wrapper = mount(Box, { 31 | global: { 32 | directives: { 33 | clickOutSide, 34 | } 35 | } 36 | }) 37 | 38 | const box = wrapper.find('.box') 39 | const main = wrapper.find('main') 40 | 41 | expect(box.exists()).toBe(true) 42 | 43 | await main.trigger('click') 44 | 45 | expect(box.isVisible()).toBe(false) 46 | }) 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mahdikhashan/vue3-click-outside", 3 | "description": "A Vue.js 3 click outside custom directive", 4 | "version": "0.1.2", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "start": "babel-node src", 8 | "build": "rimraf lib && babel src -d lib --ignore src/__tests__", 9 | "test": "jest --watch" 10 | }, 11 | "devDependencies": { 12 | "@babel/preset-env": "^7.21.4", 13 | "@vue/test-utils": "^2.3.2", 14 | "@vue/vue3-jest": "^28.1.0", 15 | "babel-cli": "^6.26.0", 16 | "babel-core": "^7.0.0-bridge.0", 17 | "babel-jest": "^28.1.3", 18 | "babel-preset-env": "^1.7.0", 19 | "jest": "^28.1.3", 20 | "jest-environment-jsdom": "^29.5.0", 21 | "rimraf": "^3.0.2", 22 | "vue": "^3.2.47" 23 | }, 24 | "peerDependencies": { 25 | "vue": "3.x.x" 26 | }, 27 | "homepage": "https://github.com/mahdikhashan/vue3-click-outside#readme", 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/mahdikhashan/vue3-click-outside" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/mahdikhashan/vue3-click-outside/issues", 34 | "email": "mahdikhashan1@gmail.com" 35 | }, 36 | "keywords": [ 37 | "vue", 38 | "vue3", 39 | "directive", 40 | "click-out-side" 41 | ], 42 | "author": "Mahdi Khashan", 43 | "license": "MIT", 44 | "jest": { 45 | "verbose": true, 46 | "moduleFileExtensions": ["js","vue"], 47 | "testEnvironmentOptions": { 48 | "customExportConditions": [ 49 | "node", 50 | "node-addons" 51 | ] 52 | }, 53 | "transform": { 54 | "^.+\\.js$": "babel-jest", 55 | "^.+\\.vue$": "@vue/vue3-jest" 56 | } 57 | }, 58 | "dependencies": { 59 | "vue": "3.x.x" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-click-outside 2 | Directive for Vue 3 to run a method on clicking outside of the binded element 3 | 4 |  5 |  6 |  7 |  8 |  9 |  10 | 11 | 12 | ## Demo 13 | 14 | [Demo](https://codesandbox.io/s/vue3-click-out-side-i6zhbb) 15 | 16 | ## Installation 17 | 18 | ``` 19 | npm install --save @mahdikhashan/vue3-click-outside 20 | ``` 21 | 22 | ## How to use 23 | add the custom directive to you component 24 | 25 | ```js 26 | 27 | 28 | 29 | BOX 30 | 31 | 32 | Outside of the box 33 | 34 | 35 | 36 | 57 | 58 | 73 | 74 | ``` 75 | 76 | ### Use Composition Api 77 | If you want to use the library with the
Outside of the box