Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add menu option #44

Merged
merged 10 commits into from Apr 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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