Skip to content

Commit

Permalink
Add menu option (#44)
Browse files Browse the repository at this point in the history
Fixes #14 
Fixes #43

Co-authored-by: Joni Van Roost <joni.vanroost@student.kdg.be>
  • Loading branch information
2 people authored and sindresorhus committed Apr 9, 2019
1 parent 36f81db commit bb1b073
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 129 deletions.
29 changes: 14 additions & 15 deletions fixture.js
@@ -1,5 +1,8 @@
'use strict';
const {app, BrowserWindow} = require('electron');
const {
app,
BrowserWindow
} = require('electron');
const contextMenu = require('.');

contextMenu({
Expand All @@ -10,30 +13,26 @@ contextMenu({
save: 'Configured Save Image',
saveImageAs: 'Configured Save Image As…',
copyLink: 'Configured Copy Link',
copyImageAddress: 'Configured Copy Image Address',
inspect: 'Configured Inspect'
},
prepend: () => [
prepend: actions => [actions.cut({transform: content => 'modified_cut_' + content})],
menu: actions => [
actions.separator(),
actions.copyLink({transform: content => 'modified_link_' + content}),
actions.separator(),
{
label: 'Unicorn'
},
{
type: 'separator'
},
{
type: 'separator'
},
actions.separator(),
actions.copy({transform: content => 'modified_copy_' + content}),
{
label: 'Invisible',
visible: false
},
{
type: 'separator'
},
{
type: 'separator'
}
actions.paste({transform: content => 'modified_paste_' + content})
],
append: () => {},
append: actions => [actions.saveImage(), actions.saveImageAs(), actions.copyImageAddress(), actions.separator(), actions.inspect()],
showCopyImageAddress: true,
showSaveImageAs: true
});
Expand Down
28 changes: 26 additions & 2 deletions index.d.ts
Expand Up @@ -48,6 +48,24 @@ export interface Labels {
readonly inspect?: string;
}

export interface ActionOptions {
/**
* Apply transformation function to the content of the action
*/
readonly transform?: (content: string) => string;
}

export interface Actions {
readonly separator: () => MenuItem;
readonly inspect: () => MenuItem;
readonly cut: (options: ActionOptions) => MenuItem;
readonly copy: (options: ActionOptions) => MenuItem;
readonly paste: (options: ActionOptions) => MenuItem;
readonly saveImage: (options: ActionOptions) => MenuItem;
readonly saveImageAs: (options: ActionOptions) => MenuItem;
readonly copyImageAddress: (options: ActionOptions) => MenuItem;
}

export interface Options {
/**
* Window or WebView to add the context menu to.
Expand All @@ -58,12 +76,18 @@ export interface Options {
/**
* Should return an array of [menu items](https://electronjs.org/docs/api/menu-item) to be prepended to the context menu.
*/
readonly prepend?: (params: ContextMenuParams, browserWindow: BrowserWindow | WebviewTag) => MenuItem[];
readonly prepend?: (defaultActions: Actions, params: ContextMenuParams, browserWindow: BrowserWindow | WebviewTag) => MenuItem[];

/**
* Should return an array of [menu items](https://electronjs.org/docs/api/menu-item) to override the default context menu.
* @default [defaultActions.cut(), defaultActions.copy(), defaultActions.paste(), defaultActions.separator(), defaultActions.saveImage(), defaultActions.saveImageAs(), defaultActions.copyImageAddress(), defaultActions.separator(), defaultActions.copyLink(), defaultActions.separator(), defaultActions.inspect()]
*/
readonly menu?: (defaultActions: Actions, params: ContextMenuParams, browserWindow: BrowserWindow | WebviewTag) => MenuItem[];

/**
* Should return an array of [menu items](https://electronjs.org/docs/api/menu-item) to be appended to the context menu.
*/
readonly append?: (param: ContextMenuParams, browserWindow: BrowserWindow | WebviewTag) => MenuItem[];
readonly append?: (defaultActions: Actions, param: ContextMenuParams, browserWindow: BrowserWindow | WebviewTag) => MenuItem[];

/**
* Show the `Copy Image Address` menu item when right-clicking on an image.
Expand Down
223 changes: 113 additions & 110 deletions index.js
Expand Up @@ -15,147 +15,140 @@ function create(win, options) {
const hasText = props.selectionText.trim().length > 0;
const can = type => editFlags[`can${type}`] && hasText;

let menuTpl = [
{
type: 'separator'
},
{
const defaultActions = {
cut: decorateMenuItem({
id: 'cut',
label: 'Cut',
// Needed because of macOS limitation:
// https://github.com/electron/electron/issues/5860
role: can('Cut') ? 'cut' : '',
enabled: can('Cut'),
visible: props.isEditable
},
{
visible: props.isEditable,
click(menuItem) {
props.selectionText = menuItem.transform ? menuItem.transform(props.selectionText) : props.selectionText;
electron.clipboard.writeText(props.selectionText);
win.webContents.delete();
}
}),
copy: decorateMenuItem({
id: 'copy',
label: 'Copy',
role: can('Copy') ? 'copy' : '',
enabled: can('Copy'),
visible: props.isEditable || hasText
},
{
visible: props.isEditable || hasText,
click(menuItem) {
props.selectionText = menuItem.transform ? menuItem.transform(props.selectionText) : props.selectionText;
electron.clipboard.writeText(props.selectionText);
}
}),
paste: decorateMenuItem({
id: 'paste',
label: 'Paste',
role: editFlags.canPaste ? 'paste' : '',
enabled: editFlags.canPaste,
visible: props.isEditable
},
{
type: 'separator'
}
];

if (props.mediaType === 'image') {
menuTpl = [
{
type: 'separator'
},
{
id: 'save',
label: 'Save Image',
click(item, win) {
download(win, props.srcURL);
}
visible: props.isEditable,
click(menuItem) {
let clipboardContent = electron.clipboard.readText(props.selectionText);
clipboardContent = menuItem.transform ? menuItem.transform(clipboardContent) : clipboardContent;
win.webContents.insertText(clipboardContent);
}
];

if (options.showSaveImageAs) {
menuTpl.push({
id: 'saveImageAs',
label: 'Save Image As…',
click(item, win) {
download(win, props.srcURL, {saveAs: true});
}
});
}

menuTpl.push({
type: 'separator'
});
}

if (props.linkURL && props.mediaType === 'none') {
menuTpl = [
{
type: 'separator'
},
{
id: 'copyLink',
label: 'Copy Link',
}),
inspect: () => {
return {
id: 'inspect',
label: 'Inspect Element',
enabled: options.showInspectElement || (options.showInspectElement !== false && isDev),
click() {
electron.clipboard.write({
bookmark: props.linkText,
text: props.linkURL
});
}
},
{
type: 'separator'
}
];
}
win.inspectElement(props.x, props.y);

if (options.showCopyImageAddress && props.mediaType === 'image') {
menuTpl.push(
{
type: 'separator'
},
{
id: 'copyImageAddress',
label: 'Copy Image Address',
click() {
electron.clipboard.write({
bookmark: props.srcURL,
text: props.srcURL
});
if (webContents(win).isDevToolsOpened()) {
webContents(win).devToolsWebContents.focus();
}
}
},
{
};
},
separator: () => {
return {
type: 'separator'
};
},
saveImage: decorateMenuItem({
id: 'save',
label: 'Save Image',
visible: props.mediaType === 'image',
click(menuItem) {
props.srcURL = menuItem.transform ? menuItem.transform(props.srcURL) : props.srcURL;
download(win, props.srcURL);
}
}),
saveImageAs: decorateMenuItem({
id: 'saveImageAs',
label: 'Save Image As…',
visible: options.showSaveImageAs && props.mediaType === 'image',
click(menuItem) {
props.srcURL = menuItem.transform ? menuItem.transform(props.srcURL) : props.srcURL;
download(win, props.srcURL, {saveAs: true});
}
);
}),
copyLink: decorateMenuItem({
id: 'copyLink',
label: 'Copy Link',
visible: props.linkURL.length !== 0 && props.mediaType === 'none',
click(menuItem) {
props.linkURL = menuItem.transform ? menuItem.transform(props.linkURL) : props.linkURL;

electron.clipboard.write({
bookmark: props.linkText,
text: props.linkURL
});
}
}),
copyImageAddress: decorateMenuItem({
id: 'copyImageAddress',
label: 'Copy Image Address',
visible: options.showCopyImageAddress && props.mediaType === 'image',
click(menuItem) {
props.srcURL = menuItem.transform ? menuItem.transform(props.srcURL) : props.srcURL;

electron.clipboard.write({
bookmark: props.srcURL,
text: props.srcURL
});
}
})
};

let menuTpl = [
defaultActions.separator(),
defaultActions.cut(),
defaultActions.copy(),
defaultActions.paste(),
defaultActions.separator(),
defaultActions.saveImage(),
defaultActions.saveImageAs(),
defaultActions.copyImageAddress(),
defaultActions.separator(),
defaultActions.copyLink(),
defaultActions.separator(),
defaultActions.inspect(),
defaultActions.separator()
];

if (options.menu) {
menuTpl = options.menu(defaultActions, props, win);
}

if (options.prepend) {
const result = options.prepend(props, win);
const result = options.prepend(defaultActions, props, win);

if (Array.isArray(result)) {
menuTpl.unshift(...result);
}
}

if (options.append) {
const result = options.append(props, win);
const result = options.append(defaultActions, props, win);

if (Array.isArray(result)) {
menuTpl.push(...result);
}
}

if (options.showInspectElement || (options.showInspectElement !== false && isDev)) {
menuTpl.push(
{
type: 'separator'
},
{
id: 'inspect',
label: 'Inspect Element',
click() {
win.inspectElement(props.x, props.y);

if (webContents(win).isDevToolsOpened()) {
webContents(win).devToolsWebContents.focus();
}
}
},
{
type: 'separator'
}
);
}

// Apply custom labels for default menu items
if (options.labels) {
for (const menuItem of menuTpl) {
Expand Down Expand Up @@ -184,6 +177,16 @@ function create(win, options) {
});
}

function decorateMenuItem(menuItem) {
return (options = {}) => {
if (options.transform && !options.click) {
menuItem.transform = options.transform;
}

return menuItem;
};
}

function delUnusedElements(menuTpl) {
let notDeletedPrevEl;
return menuTpl.filter(el => el.visible !== false).filter((el, i, array) => {
Expand Down

0 comments on commit bb1b073

Please sign in to comment.