├── .editorconfig ├── .gitignore ├── .travis.yml ├── example.html ├── index.js ├── license ├── package.json ├── readme.md └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #### joe made this: https://goel.io/joe 2 | 3 | #####=== SublimeText ===##### 4 | # cache files for sublime text 5 | *.tmlanguage.cache 6 | *.tmPreferences.cache 7 | *.stTheme.cache 8 | 9 | # workspace files are user-specific 10 | *.sublime-workspace 11 | 12 | # project files should be checked into the repository, unless a significant 13 | # proportion of contributors will probably not be using SublimeText 14 | # *.sublime-project 15 | 16 | # sftp configuration file 17 | sftp-config.json 18 | 19 | #####=== Node ===##### 20 | 21 | # Logs 22 | logs 23 | *.log 24 | 25 | # Runtime data 26 | pids 27 | *.pid 28 | *.seed 29 | 30 | # Directory for instrumented libs generated by jscoverage/JSCover 31 | lib-cov 32 | 33 | # Coverage directory used by tools like istanbul 34 | coverage 35 | 36 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 37 | .grunt 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (http://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directory 46 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 47 | node_modules 48 | 49 | # Debug log from npm 50 | npm-debug.log 51 | 52 | #####=== OSX ===##### 53 | .DS_Store 54 | .AppleDouble 55 | .LSOverride 56 | 57 | # Icon must end with two \r 58 | Icon 59 | 60 | # Thumbnails 61 | ._* 62 | 63 | # Files that might appear on external disk 64 | .Spotlight-V100 65 | .Trashes 66 | 67 | # Directories potentially created on remote AFP share 68 | .AppleDB 69 | .AppleDesktop 70 | Network Trash Folder 71 | Temporary Items 72 | .apdisk 73 | 74 | #####=== Linux ===##### 75 | *~ 76 | 77 | # KDE directory preferences 78 | .directory 79 | 80 | # Linux trash folder which might appear on any partition or disk 81 | .Trash-* 82 | 83 | 84 | yarn.lock 85 | 86 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | sudo: false 5 | -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | example 5 | 6 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |

Hola

33 | 34 | 35 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const electron = require('electron'); 4 | 5 | const DOM_VK_A = 0x41; // (65) "A" key. 6 | const DOM_VK_C = 0x43; // (67) "C" key. 7 | const DOM_VK_V = 0x56; // (86) "V" key. 8 | const DOM_VK_X = 0x58; // (88) "X" key. 9 | const DOM_VK_Z = 0x5A; // (90) "Z" key. 10 | 11 | const menuTemplate = [{ 12 | label: 'Undo', 13 | role: 'undo' 14 | }, { 15 | label: 'Redo', 16 | role: 'redo' 17 | }, { 18 | type: 'separator' 19 | }, { 20 | label: 'Cut', 21 | role: 'cut' 22 | }, { 23 | label: 'Copy', 24 | role: 'copy' 25 | }, { 26 | label: 'Paste', 27 | role: 'paste' 28 | }, { 29 | type: 'separator' 30 | }, { 31 | label: 'Select all', 32 | role: 'selectall' 33 | }]; 34 | 35 | function action(name, evt) { 36 | const win = electron.remote.getCurrentWindow(); 37 | win.webContents[name](); 38 | evt.preventDefault(); 39 | return false; 40 | } 41 | 42 | function defaultIsEditable(node) { 43 | return node.matches('input, textarea, [contenteditable]'); 44 | } 45 | 46 | function mkInputMenu(isEditable = defaultIsEditable) { 47 | function inputMenu(ctx, next) { 48 | let node = ctx.elm; 49 | 50 | while (node) { 51 | if (isEditable(node)) { 52 | [].push.apply(ctx.menu, menuTemplate); 53 | break; 54 | } 55 | node = node.parentElement; 56 | } 57 | next(); 58 | } 59 | 60 | function handleInputShortcuts(evt) { 61 | const c = evt.keyCode; 62 | const ctrlDown = evt.ctrlKey || evt.metaKey; // OSX support 63 | const altDown = evt.altKey; 64 | const shiftDown = evt.shiftKey; 65 | 66 | if (altDown) { 67 | return true; 68 | } 69 | 70 | if (!isEditable(evt.target)) { 71 | return true; 72 | } 73 | 74 | if (ctrlDown && !shiftDown && c === DOM_VK_C) { 75 | return action('copy', evt); 76 | } 77 | 78 | if (ctrlDown && !shiftDown && c === DOM_VK_V) { 79 | return action('paste', evt); 80 | } 81 | 82 | if (ctrlDown && !shiftDown && c === DOM_VK_X) { 83 | return action('cut', evt); 84 | } 85 | 86 | if (ctrlDown && !shiftDown && c === DOM_VK_A) { 87 | return action('selectAll', evt); 88 | } 89 | 90 | if (ctrlDown && !shiftDown && c === DOM_VK_Z) { 91 | return action('undo', evt); 92 | } 93 | 94 | if (ctrlDown && shiftDown && c === DOM_VK_Z) { 95 | return action('redo', evt); 96 | } 97 | 98 | return true; 99 | } 100 | 101 | function registerShortcuts() { 102 | if (document.body) { 103 | document.body.addEventListener('keydown', handleInputShortcuts); 104 | } else { 105 | document.addEventListener('DOMContentLoaded', () => { 106 | document.body.addEventListener('keydown', handleInputShortcuts); 107 | }); 108 | } 109 | } 110 | 111 | inputMenu.registerShortcuts = registerShortcuts; 112 | return inputMenu; 113 | } 114 | 115 | const defaultInputMenu = mkInputMenu(); 116 | defaultInputMenu.mkInputMenu = mkInputMenu; 117 | defaultInputMenu.defaultIsEditable = defaultIsEditable; 118 | module.exports = defaultInputMenu; 119 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 parro-it 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, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 21 | OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-input-menu", 3 | "version": "2.1.0", 4 | "description": "Context menu for electron input elements.", 5 | "repository": "parro-it/electron-input-menu", 6 | "license": "MIT", 7 | "author": "andrea@parro.it", 8 | "scripts": { 9 | "test": "xo", 10 | "start": "electron test.js" 11 | }, 12 | "keywords": [ 13 | "electron", 14 | "input", 15 | "contextmenu", 16 | "shortcuts" 17 | ], 18 | "eslintConfig": { 19 | "extends": [ 20 | "js", 21 | "features" 22 | ] 23 | }, 24 | "devDependencies": { 25 | "electron-contextmenu-middleware": "^1.0.0", 26 | "electron-prebuilt": "^1.1.1", 27 | "xo": "^0.17.1" 28 | }, 29 | "files": [ 30 | "index.js" 31 | ], 32 | "xo": { 33 | "envs": [ 34 | "browser", 35 | "node" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # electron-input-menu 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/parro-it/electron-input-menu.svg)](https://greenkeeper.io/) 4 | 5 | > Context menu for [electron](https://github.com/atom/electron) input elements. 6 | 7 | [![Travis Build Status](https://img.shields.io/travis/parro-it/electron-input-menu.svg)](http://travis-ci.org/parro-it/electron-input-menu) 8 | [![NPM module](https://img.shields.io/npm/v/electron-input-menu.svg)](https://npmjs.org/package/electron-input-menu) 9 | [![NPM downloads](https://img.shields.io/npm/dt/electron-input-menu.svg)](https://npmjs.org/package/electron-input-menu) 10 | 11 | # Installation 12 | 13 | ```bash 14 | npm install --save electron-input-menu 15 | ``` 16 | 17 | # Usage 18 | 19 | This module expose a middleware for [electron-contextmenu-middleware](https://github.com/parro-it/electron-contextmenu-middleware). 20 | 21 | To use input context menu, you have to require this module and `electron-contextmenu-middleware` in `renderer` process and then mount this module as a middleware. 22 | 23 | ```js 24 | const inputMenu = require('electron-input-menu'); 25 | const context = require('electron-contextmenu-middleware'); 26 | 27 | context.use(inputMenu); 28 | 29 | context.activate(); 30 | ``` 31 | 32 | # Keyboard shortcuts 33 | 34 | `electron-input-menu` can also register shortcuts on DOM `document` object to handle copy, paste, cut, selectAll, undo and redo action. This is useful if your app doesn't provide an "Edit" menu that can handle this shortcuts. 35 | 36 | To activate the shortcuts, call the `registerShortcuts` method in renderer process. 37 | 38 | ```js 39 | const inputMenu = require('electron-input-menu'); 40 | inputMenu.registerShortcuts(); 41 | ``` 42 | 43 | 44 | # Related projects 45 | 46 | * [electron-contextmenu-middleware](https://github.com/parro-it/electron-contextmenu-middleware) - Build `electron` context menus composing multiple middlewares functions. 47 | 48 | * [debug-menu](https://github.com/parro-it/debug-menu) - Chrome-like "inspect element" context-menu. 49 | 50 | 51 | 52 | # License 53 | 54 | The MIT License (MIT) 55 | 56 | Copyright (c) 2016 parro-it 57 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const electron = require('electron'); 3 | 4 | electron.app.on('ready', () => { 5 | const win = new electron.BrowserWindow({ 6 | show: true 7 | }); 8 | const empty = electron.Menu.buildFromTemplate([]); 9 | win.setMenu(empty); 10 | electron.Menu.setApplicationMenu(empty); 11 | win.loadURL(`file://${__dirname}/example.html`); 12 | win.webContents.openDevTools(); 13 | }); 14 | --------------------------------------------------------------------------------