├── .gitignore ├── CHANGELOG ├── LICENSE ├── README.md ├── example.png ├── index.js ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v3.0.1 2 | 3 | * updated electron@7 compatibility 4 | 5 | v3.0.0 6 | 7 | * Add missing quit menu item on macOs 8 | 9 | v2.0.0 10 | 11 | * hide "about" menu item and "help" menu on non-mac platforms 12 | * introduce electron back as peerDependency 13 | 14 | v1.1.1 15 | 16 | * remove electron as dependency 17 | 18 | v1.1.0 19 | 20 | * Menu now returns the Electron menu instance 21 | 22 | v1.0.0 Initial release 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License (ISC) 2 | Copyright 2019, Kilian Valkhof 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 11 | SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 12 | RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 13 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 14 | USE OR PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Made by [@kilianvalkhof](https://twitter.com/kilianvalkhof) 2 | 3 | #### Other projects: 4 | 5 | - 💻 [Polypane](https://polypane.app) - Develop responsive websites and apps twice as fast on multiple screens at once 6 | - 🖌️ [Superposition](https://superposition.design) - Kickstart your design system by extracting design tokens from your website 7 | - 🗒️ [FromScratch](https://fromscratch.rocks) - A smart but simple autosaving scratchpad 8 | 9 | --- 10 | 11 | # Electron-create-menu [![npm](https://img.shields.io/npm/v/electron-create-menu.svg)](https://www.npmjs.com/package/electron-create-menu) [![npm-downloads](https://img.shields.io/npm/dm/electron-create-menu.svg)](https://www.npmjs.com/package/electron-create-menu) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FKilian%2Felectron-create-menu.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FKilian%2Felectron-create-menu?ref=badge_shield) 12 | 13 | provides a default menu for your electron applications, with convenience functions for multiplatform use and i18n. 14 | 15 | 16 | 17 | ## Installation 18 | 19 | Install using `npm install electron-create-menu`. 20 | 21 | ## Usage 22 | 23 | Instead of importing Menu from `electron`, import it from `electron-create-menu`: 24 | 25 | ```js 26 | import Menu from "electron-create-menu"; 27 | // or 28 | const Menu = require("electron-create-menu"); 29 | ``` 30 | 31 | To get a default menu with platform-appropriate menu items and submenus, call Menu like so: 32 | 33 | ``` 34 | Menu(); 35 | ``` 36 | 37 | _Note:_ This API has to be called after the `ready` event of `app` module. 38 | 39 | Menu always returns the menu object that `Menu.buildFromTemplate` creates, so you can access [instance methods](https://electronjs.org/docs/api/menu#instance-methods) on it. 40 | 41 | ### Optional arguments 42 | 43 | Menu has two optional functions you can pass it 44 | 45 | - The first argument is the `callback` function, where you can further edit (or replace) the generated menu. 46 | - The second argument is the `i18n` function where you can supply a function to use for translating the menu items. 47 | 48 | ```js 49 | Menu(callback, i18n); 50 | ``` 51 | 52 | ### The callback function 53 | 54 | `callback` receives two arguments: 55 | 56 | - The generated menu 57 | - A function that returns `{type: 'separator'}` for convenience. 58 | 59 | It expects you to return a menu-like object, either the edited default menu or a new menu. 60 | 61 | #### Callback example 62 | 63 | To append a menu item to the menu, push an object onto menu and return it: 64 | 65 | ```js 66 | Menu((defaultMenu, separator) => { 67 | defaultMenu.push({ 68 | label: "My custom menu!", 69 | submenu: [ 70 | { label: "my first item" }, 71 | separator(), 72 | { label: "my second item" } 73 | ] 74 | }); 75 | 76 | return defaultMenu; 77 | }); 78 | ``` 79 | 80 | ### The i18n function 81 | 82 | The i18n function is applied to the labels of the default menu. There are two things worth mentioning: 83 | 84 | - Most items in the default menu are specified by a _role_, so the OS will supply the translation. 85 | - Labels added in the callback function are not translated by this function. 86 | 87 | #### Example using i18next 88 | 89 | ```js 90 | const i18next = require('i18next'); 91 | 92 | i18next.init({ 93 | /* assumed setup of i18next here */ 94 | }).then(function(t) { 95 | 96 | Menu( 97 | menu => { 98 | menu.push({ 99 | label: i18next.t('My custom menu!'), 100 | submenu: [ 101 | {label: i18next.t('my first item')}, 102 | {label: i18next.t('my second item')}, 103 | ], 104 | }), 105 | 106 | return menu; 107 | }, 108 | 109 | // This function is used to translate the default labels 110 | i18next.t 111 | ); 112 | 113 | }); 114 | ``` 115 | 116 | ## Multiplatform use 117 | 118 | Each item in your menu can have two new properties, `showOn` and `hideOn`. These accept a string or an array of strings that correspond to `process.platform` values such as 'darwin' or 'win32'. 119 | 120 | ```js 121 | // this shows the menu item only on macOs 122 | { 123 | showOn: "darwin"; 124 | } 125 | 126 | // this hides the menu item on windows and macOs 127 | { 128 | hideOn: ["win32", "darwin"]; 129 | } 130 | ``` 131 | 132 | With these, you can adapt your menu to multiple platforms without having to maintain multiple menu templates. See the [default template](https://github.com/Kilian/electron-create-menu/blob/master/index.js#L7) for an example of a consolidated template. 133 | 134 | You can also add a string or an array of strings as an argument to the separator function: `separator('darwin')`. The given value is interpreted as the value for `showOn`. 135 | 136 | #### Example 137 | 138 | ```js 139 | Menu((defaultMenu, separator) => { 140 | 141 | defaultMenu.push({ 142 | label: "My custom menu!", 143 | submenu: [ 144 | { 145 | label: 'This is only shown on macOs', 146 | showOn: 'darwin', 147 | }, 148 | separator('darwin'), // this is shown only macOs 149 | {label: 150 | 'This is hidden on windows' 151 | hideOn: ['win32'] 152 | }, 153 | ], 154 | }); 155 | 156 | return defaultMenu; 157 | }); 158 | ``` 159 | 160 | ## License 161 | 162 | Electron-create-menu is ISC licensed. 163 | 164 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FKilian%2Felectron-create-menu.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FKilian%2Felectron-create-menu?ref=badge_large) 165 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kilian/electron-create-menu/b37146107775a3c02fa00fd19ff9c9b1801e1180/example.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { app, Menu } = require("electron"); 2 | 3 | const PLATFORM = process.platform; 4 | const SEPARATOR = platform => ({ type: "separator", showOn: platform }); 5 | const passThrough = value => value; 6 | 7 | const createTemplate = i18nFunc => [ 8 | { 9 | label: app.name, 10 | showOn: ["darwin"], 11 | submenu: [ 12 | { role: "about" }, 13 | SEPARATOR(), 14 | { role: "services" }, 15 | SEPARATOR(), 16 | { role: "hide" }, 17 | { role: "hideothers" }, 18 | { role: "unhide" }, 19 | SEPARATOR(), 20 | { role: "quit" } 21 | ] 22 | }, 23 | { 24 | label: i18nFunc("File"), 25 | hideOn: ["darwin"], 26 | submenu: [{ role: "quit" }] 27 | }, 28 | { 29 | label: i18nFunc("Edit"), 30 | submenu: [ 31 | { role: "undo" }, 32 | { role: "redo" }, 33 | SEPARATOR(), 34 | { role: "cut" }, 35 | { role: "copy" }, 36 | { role: "paste" }, 37 | { role: "pasteandmatchstyle" }, 38 | { role: "delete" }, 39 | { role: "selectall" }, 40 | SEPARATOR("darwin"), 41 | { 42 | label: i18nFunc("Speech"), 43 | showOn: ["darwin"], 44 | submenu: [{ role: "startspeaking" }, { role: "stopspeaking" }] 45 | } 46 | ] 47 | }, 48 | { 49 | label: i18nFunc("View"), 50 | submenu: [ 51 | { role: "reload" }, 52 | { role: "forcereload" }, 53 | { role: "toggledevtools" }, 54 | SEPARATOR(), 55 | { role: "resetzoom" }, 56 | { role: "zoomin" }, 57 | { role: "zoomout" }, 58 | SEPARATOR(), 59 | { role: "togglefullscreen" } 60 | ] 61 | }, 62 | { 63 | role: "window", 64 | submenu: [ 65 | { role: "minimize" }, 66 | { role: "close" }, 67 | { role: "zoom", showOn: ["darwin"] }, 68 | SEPARATOR("darwin"), 69 | { role: "front", showOn: ["darwin"] } 70 | ] 71 | }, 72 | { 73 | role: "help", 74 | showOn: ["darwin"], 75 | submenu: [] 76 | } 77 | ]; 78 | 79 | const shouldShowItem = item => { 80 | let shouldShow = item.hideOn || item.showOn ? false : true; 81 | 82 | if (item.hideOn) { 83 | if (typeof item.hideOn === "string") { 84 | shouldShow = item.hideOn !== PLATFORM; 85 | } else if (Array.isArray(item.hideOn)) { 86 | shouldShow = !item.hideOn.includes(PLATFORM); 87 | } else { 88 | throw Error( 89 | `hideOn for item "${JSON.stringify(item)}" is not a string or an array` 90 | ); 91 | } 92 | } 93 | 94 | if (item.showOn) { 95 | if (typeof item.showOn === "string") { 96 | shouldShow = item.showOn === PLATFORM; 97 | } else if (Array.isArray(item.showOn)) { 98 | shouldShow = item.showOn.includes(PLATFORM); 99 | } else { 100 | throw Error( 101 | `showOn for item "${JSON.stringify(item)}" is not a string or an array` 102 | ); 103 | } 104 | } 105 | 106 | return shouldShow; 107 | }; 108 | 109 | const filterMenu = list => 110 | list 111 | .map(item => { 112 | if (item.submenu) { 113 | item.submenu = filterMenu(item.submenu); 114 | } 115 | return shouldShowItem(item) ? item : false; 116 | }) 117 | .filter(x => x); 118 | 119 | const electronMenu = (callback = passThrough, i18nFunc = passThrough) => { 120 | const createdMenu = Menu.buildFromTemplate( 121 | filterMenu(callback(createTemplate(i18nFunc), SEPARATOR)) 122 | ); 123 | 124 | Menu.setApplicationMenu(createdMenu); 125 | 126 | return createdMenu; 127 | }; 128 | 129 | module.exports = electronMenu; 130 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-create-menu", 3 | "version": "3.0.1", 4 | "description": "provides a default menu for your electron applications, with convenience functions for multiplatform use and i18n.", 5 | "main": "index.js", 6 | "files": [], 7 | "keywords": [ 8 | "electron", 9 | "menu", 10 | "i18n", 11 | "multiplatform", 12 | "os" 13 | ], 14 | "author": "Kilian Valkhof", 15 | "license": "ISC", 16 | "peerDependencies": { 17 | "electron": "> 2" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/kilian/electron-create-menu.git" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | --------------------------------------------------------------------------------