├── .eslintignore ├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── misc └── keypressLogo.png ├── packages ├── demo │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierrc.json │ ├── README.md │ ├── package.json │ ├── public │ │ ├── emptyKey.png │ │ ├── index.html │ │ └── keypressLogo.png │ ├── src │ │ ├── App.vue │ │ ├── components │ │ │ ├── KeyBackground.vue │ │ │ └── KeypressHandler.vue │ │ ├── main.ts │ │ └── shims-vue.d.ts │ ├── tsconfig.json │ ├── vue.config.js │ └── yarn.lock └── vue3-keypress │ ├── .eslintrc.js │ ├── .npmignore │ ├── .prettierrc.json │ ├── @types │ └── index.ts │ ├── package.json │ ├── src │ ├── key_codes.ts │ └── vue3_keypress.ts │ ├── tsconfig.json │ └── yarn.lock └── vetur.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | lib/** -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Enables "Sponsor" button on project 2 | 3 | github: lupas 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | lib -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | Downloads 5 | Version 6 | License 7 |

8 |

9 | 10 | # ⚠️ Breaking Changes with V4 ⚠️ 11 | 12 | Version 4 introduced breaking changes by making use of the Vue 3 composition API and dropping the component-based approach. 13 | Follow the guide below to setup the module following the new approach. 14 | 15 | # Vue Keypress 16 | 17 | Want to capture _keydown_, _keypress_ and _keyup_ and events globally in Vue? Nothing easier than that. 18 | 19 | The Vue Keypress Component let's you do just that. 20 | 21 | Just add the component to the view/component that should start a global keypress handler. When the component gets destroyed, the global event handler also gets removed. 22 | 23 | # ‼️ Using Vue 2? 24 | 25 | This package only supports Vue 3. 26 | 27 | For Vue 2 support, visit the [lupas/vue-keypress](https://github.com/lupas/vue-keypress) package repository. 28 | 29 | # How to install? 30 | 31 | ```bsh 32 | yarn add vue3-keypress 33 | // or 34 | npm i vue3-keypress 35 | ``` 36 | 37 | # How to use in your project? 38 | 39 | # Simple Example 40 | 41 | ```vue 42 | 62 | ``` 63 | 64 | # Full Example 65 | 66 | ```vue 67 | 117 | ``` 118 | 119 | # Config 120 | 121 | | Variable | Type | Default | Possible Values | Description | 122 | | ---------- | ------------ | ------- | ------------------------------ | --------------------------------------------------------------------------------- | 123 | | keyEvent | String | 'keyup' | _keydown_, _keypress_, _keyup_ | | 124 | | keyBinds | KeyBind[] | [] | see below | Object containing infos about which keys are expected to be pressed. | 125 | | isActive | Ref(Boolean) | false | true / false | Setups up a listener that activates/deactivates the keypress listener. | 126 | | onWrongKey | Function | null | | Callback that is triggered every time a key is pressed that is not in "keyBinds". | 127 | | onAnyKey | Function | null | | Callback that is triggered every time a key is pressed. | 128 | 129 | ## Key Binds 130 | 131 | | Variable | Type | Default | Possible Values | Description | 132 | | -------------- | --------------- | ------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------- | 133 | | keyCode | Number / String | null | [KeyCode as integer](https://keycode.info/) or a [mapped string](https://github.com/lupas/vue3-keypress/blob/master/packages/vue3-keypress/src/key_codes.ts) | Key that should trigger the event. If _null_, any key will trigger event. | 134 | | modifiers | Array | [] | ['_ctrlKey_', '_shiftKey_', '_altKey_', '_metaKey_'] | Keys that needs to be pressed down before the actual key (key Code), e.g. Ctrl+A. | 135 | | preventDefault | Boolean | true | _true_,_false_ | Prevent the default action of the event | 136 | | success | Function | null | | Callback that is triggered when the correct key is pressed. | 137 | 138 | The return payload of the callbacks is like so: 139 | 140 | ```js 141 | { 142 | event: Object, // the official event object 143 | expectedEvent: Object, // your defined props. 144 | message: String // A declarative message on error/success. 145 | } 146 | ``` 147 | -------------------------------------------------------------------------------- /misc/keypressLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupas/vue3-keypress/7a4bd680493e3d2ffaa72e1d42d2ef8a66c54d9b/misc/keypressLogo.png -------------------------------------------------------------------------------- /packages/demo/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | extends: [ 8 | 'plugin:vue/vue3-essential', 9 | 'eslint:recommended', 10 | '@vue/typescript/recommended', 11 | 'prettier', 12 | 'prettier/vue', 13 | 'plugin:prettier/recommended' 14 | ], 15 | // add your custom rules here 16 | rules: { 17 | 'no-console': 'off', 18 | 'no-unused-vars': 'warn', 19 | 'object-shorthand': 'warn', 20 | '@typescript-eslint/member-delimiter-style': 'off' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/demo/.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 | -------------------------------------------------------------------------------- /packages/demo/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /packages/demo/README.md: -------------------------------------------------------------------------------- 1 | # demo2 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /packages/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "keypressdemo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "vue": "^3.0.0", 12 | "vue3-keypress": "^4.0.0-beta3" 13 | }, 14 | "devDependencies": { 15 | "@typescript-eslint/eslint-plugin": "^2.33.0", 16 | "@typescript-eslint/parser": "^2.33.0", 17 | "@vue/cli-plugin-eslint": "~4.5.0", 18 | "@vue/cli-plugin-typescript": "~4.5.0", 19 | "@vue/cli-service": "~4.5.0", 20 | "@vue/compiler-sfc": "^3.0.0", 21 | "@vue/eslint-config-prettier": "^6.0.0", 22 | "@vue/eslint-config-typescript": "^5.0.2", 23 | "eslint": "^6.7.2", 24 | "eslint-plugin-prettier": "^3.1.3", 25 | "eslint-plugin-vue": "^7.0.0-0", 26 | "prettier": "^1.19.1", 27 | "typescript": "~3.9.3" 28 | }, 29 | "browserslist": [ 30 | "> 1%", 31 | "last 2 versions", 32 | "not dead" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /packages/demo/public/emptyKey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupas/vue3-keypress/7a4bd680493e3d2ffaa72e1d42d2ef8a66c54d9b/packages/demo/public/emptyKey.png -------------------------------------------------------------------------------- /packages/demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/demo/public/keypressLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lupas/vue3-keypress/7a4bd680493e3d2ffaa72e1d42d2ef8a66c54d9b/packages/demo/public/keypressLogo.png -------------------------------------------------------------------------------- /packages/demo/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /packages/demo/src/components/KeyBackground.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | 29 | 47 | -------------------------------------------------------------------------------- /packages/demo/src/components/KeypressHandler.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 78 | -------------------------------------------------------------------------------- /packages/demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /packages/demo/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /packages/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": ["webpack-env"], 15 | "paths": { 16 | "@/*": ["src/*"] 17 | }, 18 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"] 19 | }, 20 | "include": [ 21 | "src/**/*.ts", 22 | "src/**/*.tsx", 23 | "src/**/*.vue", 24 | "tests/**/*.ts", 25 | "tests/**/*.tsx" 26 | ], 27 | "exclude": ["node_modules", "lib"] 28 | } 29 | -------------------------------------------------------------------------------- /packages/demo/vue.config.js: -------------------------------------------------------------------------------- 1 | // vue.config.js 2 | module.exports = { 3 | // ... 4 | configureWebpack: { 5 | resolve: { symlinks: false } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/vue3-keypress/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint', 9 | }, 10 | extends: [ 11 | 'plugin:vue/vue3-essential', 12 | 'eslint:recommended', 13 | '@vue/typescript/recommended', 14 | 'prettier', 15 | 'prettier/vue', 16 | 'plugin:prettier/recommended', 17 | ], 18 | // add your custom rules here 19 | rules: { 20 | 'no-console': 'off', 21 | 'no-unused-vars': 'warn', 22 | 'object-shorthand': 'warn', 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /packages/vue3-keypress/.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | src 4 | .eslintrc.js 5 | .prettierrc.json 6 | tsconfig.json 7 | yarn.lock -------------------------------------------------------------------------------- /packages/vue3-keypress/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /packages/vue3-keypress/@types/index.ts: -------------------------------------------------------------------------------- 1 | interface KeyBind { 2 | keyCode: string 3 | modifiers?: ['altKey' | 'metaKey' | 'ctrlKey' | 'shiftKey'] 4 | preventDefault: boolean 5 | success: { (SuccessResult): void } 6 | } 7 | 8 | interface KeypressOptions { 9 | keyEvent: 'keydown' | 'keypress' | 'keyup' 10 | keyBinds: KeyBind[] 11 | isActive?: any // TODO 12 | onAnyKey?: { (SuccessResult): void } 13 | onWrongKey?: { (SuccessResult): void } 14 | } 15 | 16 | interface KeypressResult { 17 | event: KeypressResult 18 | keyEvent: string 19 | } 20 | 21 | interface SuccessResult extends KeypressResult { 22 | keyCode: string 23 | keyEvent: string 24 | modifiers: ['altKey' | 'metaKey' | 'ctrlKey' | 'shiftKey'] 25 | preventDefault: boolean 26 | } 27 | -------------------------------------------------------------------------------- /packages/vue3-keypress/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-keypress", 3 | "version": "4.0.1", 4 | "description": "Global keypress event handler component for Vue.js 3 applications.", 5 | "author": "Pascal Luther", 6 | "license": "MIT", 7 | "homepage": "https://github.com/lupas/vue-keypress", 8 | "repository": "https://github.com/lupas/vue-keypress", 9 | "keywords": [ 10 | "vue, keypress, keydown, keyup" 11 | ], 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "scripts": { 16 | "build": "tsc", 17 | "publishToNpm": "npm run build && npm publish", 18 | "demo": "cd demo && yarn dev" 19 | }, 20 | "main": "lib/src/vue3_keypress.js", 21 | "devDependencies": { 22 | "@typescript-eslint/eslint-plugin": "^4.13.0", 23 | "@typescript-eslint/parser": "^4.13.0", 24 | "@vue/eslint-config-typescript": "^7.0.0", 25 | "babel-eslint": "^10.1.0", 26 | "eslint": "^7.16.0", 27 | "eslint-config-prettier": "^7.1.0", 28 | "eslint-loader": "^4.0.2", 29 | "eslint-plugin-prettier": "^3.3.0", 30 | "eslint-plugin-vue": "^7.4.0", 31 | "prettier": "^2.2.1", 32 | "vue": "^3.0.5", 33 | "@vue/cli-plugin-typescript": "^4.5.10", 34 | "typescript": "^4.1.3" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/vue3-keypress/src/key_codes.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | backspace: 8, 3 | tab: 9, 4 | enter: 13, 5 | shift: 16, 6 | ctrl: 17, 7 | alt: 18, 8 | 'pause/break': 19, 9 | 'caps lock': 20, 10 | esc: 27, 11 | space: 32, 12 | 'page up': 33, 13 | 'page down': 34, 14 | end: 35, 15 | home: 36, 16 | left: 37, 17 | up: 38, 18 | right: 39, 19 | down: 40, 20 | insert: 45, 21 | delete: 46, 22 | command: 91, 23 | 'left command': 91, 24 | 'right command': 93, 25 | 'numpad *': 106, 26 | 'numpad +': 107, 27 | 'numpad -': 109, 28 | 'numpad .': 110, 29 | 'numpad /': 111, 30 | 'num lock': 144, 31 | 'scroll lock': 145, 32 | 'my computer': 182, 33 | 'my calculator': 183, 34 | ';': 186, 35 | '=': 187, 36 | ',': 188, 37 | '-': 189, 38 | '.': 190, 39 | '/': 191, 40 | '`': 192, 41 | '[': 219, 42 | '\\': 220, 43 | ']': 221, 44 | "'": 222, 45 | } 46 | -------------------------------------------------------------------------------- /packages/vue3-keypress/src/vue3_keypress.ts: -------------------------------------------------------------------------------- 1 | import { watch, onMounted, onUnmounted } from 'vue' 2 | import keyCodes from './key_codes' 3 | 4 | const supportedModifiers = ['altKey', 'metaKey', 'ctrlKey', 'shiftKey'] 5 | 6 | export const useKeypress = ({ 7 | keyEvent, 8 | keyBinds, 9 | onAnyKey, 10 | onWrongKey, 11 | isActive: isListenerActiveRef, 12 | }: KeypressOptions) => { 13 | let eventListener = null 14 | 15 | for (const keyBind of keyBinds) { 16 | keyBind.keyCode = keyCodes[keyBind.keyCode] || keyBind.keyCode 17 | } 18 | 19 | const requiredModifiersPressed = (event, modifiers) => { 20 | return supportedModifiers.every( 21 | (modifier) => event[modifier] == (modifiers.indexOf(modifier) !== -1) 22 | ) 23 | } 24 | 25 | const anyModifiersPress = (event, modifiers) => { 26 | return supportedModifiers.some((modifier) => !!event[modifier]) 27 | } 28 | 29 | const eventHandler = () => { 30 | return (event) => { 31 | const callbackData = (eventData = {}) => ({ 32 | event, 33 | keyEvent, 34 | ...eventData, 35 | }) 36 | 37 | if (typeof onAnyKey == 'function') 38 | onAnyKey(callbackData(event) as KeypressResult) 39 | 40 | for (const { 41 | keyCode, 42 | modifiers, 43 | success, 44 | preventDefault = true, 45 | } of keyBinds) { 46 | // Check if the correct keys have been clicked: 47 | if (keyCode !== event.keyCode) continue 48 | 49 | if (modifiers && modifiers.length > 0) { 50 | if (!requiredModifiersPressed(event, modifiers)) continue 51 | } else { 52 | if (anyModifiersPress(event, modifiers)) continue 53 | } 54 | 55 | if (preventDefault) event.preventDefault() 56 | 57 | // SUCCESS -> the correct key was pressed if we got to this point 58 | success( 59 | callbackData({ 60 | keyCode, 61 | modifiers, 62 | preventDefault, 63 | event, 64 | } as SuccessResult) 65 | ) 66 | 67 | return !preventDefault 68 | } 69 | if (typeof onWrongKey == 'function') 70 | onWrongKey(callbackData(event) as KeypressResult) 71 | return null 72 | } 73 | } 74 | 75 | const addListener = () => { 76 | if (!eventListener) eventListener = eventHandler() 77 | window.addEventListener(keyEvent, eventListener) 78 | } 79 | 80 | const removeListener = () => { 81 | if (!eventListener) return 82 | window.removeEventListener(keyEvent, eventListener) 83 | } 84 | 85 | if (isListenerActiveRef) { 86 | if (isListenerActiveRef.value) addListener() 87 | watch(isListenerActiveRef, (active) => { 88 | active ? addListener() : removeListener() 89 | }) 90 | } else { 91 | onMounted(() => addListener()) 92 | } 93 | 94 | onUnmounted(() => removeListener()) 95 | } 96 | -------------------------------------------------------------------------------- /packages/vue3-keypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 4 | "moduleResolution": "node", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "noImplicitReturns": true, 8 | "outDir": "lib", 9 | "sourceMap": true, 10 | "skipLibCheck": true, 11 | "declaration": true 12 | }, 13 | "compileOnSave": false, 14 | "include": [ 15 | "src/**/*.ts", 16 | "src/**/*.tsx", 17 | "src/**/*.vue", 18 | "@types/*.ts", 19 | "@types/**/*.ts" 20 | ], 21 | "exclude": ["node_modules", "lib"] 22 | } 23 | -------------------------------------------------------------------------------- /vetur.config.js: -------------------------------------------------------------------------------- 1 | // vetur.config.js 2 | /** @type {import('vls').VeturConfig} */ 3 | module.exports = { 4 | settings: {}, 5 | // support monorepos 6 | projects: ["./packages/demo", "./packages/vue3-keypress"], 7 | }; 8 | --------------------------------------------------------------------------------