I am trying to write a simple menu with Electron js and I notice that the name of the first menu is different from what I code.
This is my main.js:
const electron = require('electron')
const url = require('url')
const path = require('path')
const {app, BrowserWindow, Menu} = electron;
let mainWindow;
const mainMenuTemplate = [
{
label:'File',
submenu: [
{
label: 'Custom Undo',
role: 'undo'
},
]
},
{
label:'View',
submenu: [
{
role: 'reload'
},
]
}
];
// Build menu from template
const menu = Menu.buildFromTemplate(mainMenuTemplate);
// Insert
Menu.setApplicationMenu(menu);
// Listen
app.on('ready', function(){
// main window
mainWindow = new BrowserWindow({width: 800, height: 600});
// load html into
mainWindow.loadURL(url.format ({
pathname: path.join(__dirname, 'mainWindow.html'),
protocol: 'file:',
slashes: true
}))
})
And this is the view of the application; notice the first item is named Electron instead of File.
The second menu item View is just fine. Any idea why this happens?
On macOS, the first menu item in the application menu is always named like the application itself (source). You should probably allow the first menu item to be what is customary on macOS, and then follow with the File menu.
To do that, but to also work cross-platform, you can do something along these lines:
const { app, Menu } = require('electron')
const isMac = process.platform === 'darwin'
const mainMenuTemplate = [
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
{
label:'File',
submenu: [
{
label: 'Custom Undo',
role: 'undo'
},
]
},
{
label:'View',
submenu: [
{
role: 'reload'
},
]
}
];
Code adapted from example code in Electron docs and OP's code.
Depending on the OS, the menu will either consist of 3 top-level objects (macOS) or 2 (non-macOS).
Related
I have this code in my chrome extension backround.js file
const bannedHosts = [];
const setupContextMenus = () => {
chrome.contextMenus.create({
id: 'blockHost',
type: 'normal',
contexts: ['all'],
title: 'Block website',
})
chrome.contextMenus.create({
id: 'unblockHost',
type: 'normal',
contexts: ['all'],
title: 'Unblock website',
})
}
chrome.runtime.onInstalled.addListener( () => {
console.log('extension installed')
setupContextMenus()
})
chrome.contextMenus.onClicked.addListener( ( clickData) => {
if( clickData.menuItemId === 'blockHost' ) {
console.log(clickData)
bannedHosts.push(clickData.pageUrl)
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: bannedHosts.map((h, i) => i + 1),
addRules: bannedHosts.map((h, i) => ({
id: i + 1,
action: {type: 'redirect', redirect: {extensionPath: '/options.html'}},
condition: {urlFilter: `||${h}/`, resourceTypes: ['main_frame', 'sub_frame']},
}))
});
console.log(bannedHosts)
} else if( clickData.menuItemId === 'unblockHost' ) {
...
}
})
I'm doing a quick test of the code by installing the extension, I'm able to see the context menu voices and I'm able to track the click event. My problem is with the declarativeNerRequest, I've noticed that when an url is added to the array blockedHosts, the rule that I want to add that is supposed to redirect the user to the options page will not work and if I try to visit the website it will be displayed instead of redirecting the user.
I have declared the correct permission in manifest v3, how I can fix this problem? How I add correctly a new url and a rule to block it and how I can remove the related url and rule if needed?
manifest file
import { defineManifest } from '#crxjs/vite-plugin'
export default defineManifest({
name: '',
description: '',
version: '1.0.0',
manifest_version: 3,
icons: {
16: 'img/logo-16.png',
32: 'img/logo-34.png',
48: 'img/logo-48.png',
128: 'img/logo-128.png',
},
action: {
// default_popup: 'popup.html',
// default_icon: 'img/logo-48.png',
},
options_page: 'options.html',
background: {
service_worker: 'src/background/index.js',
type: 'module',
},
content_scripts: [
{
matches: ['http://*/*', 'https://*/*'],
js: ['src/content/index.js'],
},
],
host_permissions: [
'<all_urls>'
],
web_accessible_resources: [
{
resources: [
'img/logo-16.png',
'img/logo-34.png',
'img/logo-48.png',
'img/logo-128.png',
'options.html'
],
matches: [
'<all_urls>'
],
},
],
permissions: [
'tabs',
'contextMenus',
'webRequest',
'webRequestBlocking',
'declarativeNetRequest'
]
})
I try to create sidebar component in React and I have a data structure like bellow
const links = [
{
label: 'Item-1',
url: '/item-1',
},
{
label: 'Item-2',
children: [
{
label: 'Item-2-1',
url: '/item-2-1',
},
{
label: 'Item-2-2',
url: '/item-2-2',
},
{
label: 'Item-2-3',
url: '/item-2-3',
},
],
},
{
label: 'Item-3',
children: [
{
label: 'Item-3-1',
url: '/item-3-1',
},
{
label: 'Item-3-2',
url: '/item-3-2',
},
],
},
];
So the problem is let's say the user changed URL and URL something like that http://localhost:90/item-2-3.
And I need to activate this sidebar item from the sidebar and it can be nested. So firstly I need to deactivate all other sidebar items (I don't want multiple selected items in the sidebar) and after that activate the selected sidebar item.
Because that (if I'm correct) I need update all tree items let's say I add active:false field to all JSON and after that I need to find the correct tree item from tree (URL === item-2-3) add active:true and also update all parent json to active:true (for there are look selected/opened)
So my question is am I correct if I correct how can write this code optimal way? :/
Actually, I want to create a function and when calling this function like that selectItemFromTree(links, '/item-2-2') I get results like in bellow.
const links = [
{
label: 'Item-1',
url: '/item-1',
active: false
},
{
label: 'Item-2',
active: true,
children: [
{
label: 'Item-2-1',
url: '/item-2-1',
active: false
},
{
label: 'Item-2-2',
url: '/item-2-2',
active: true,
},
{
label: 'Item-2-3',
url: '/item-2-3',
active: false
},
],
},
{
label: 'Item-3',
active: false,
children: [
{
label: 'Item-3-1',
url: '/item-3-1',
active: false
},
{
label: 'Item-3-2',
url: '/item-3-2',
active: false
},
],
},
];
You have to traverse the tree recursively and update all the active statuses.
const links=[{label:"Item-1",url:"/item-1"},{label:"Item-2",children:[{label:"Item-2-1",url:"/item-2-1"},{label:"Item-2-2",url:"/item-2-2"},{label:"Item-2-3",url:"/item-2-3"}]},{label:"Item-3",children:[{label:"Item-3-1",url:"/item-3-1"},{label:"Item-3-2",url:"/item-3-2"}]}];
const updateActiveLink = (links, url, parent = null) => {
for (link of links) {
link.active = false;
if (link?.children) {
updateActiveLink(link.children, url, link)
}
if (link.url === url) {
link.active = true;
if (!!parent) parent.active = true;
}
}
}
updateActiveLink(links, '/item-2-3');
console.log(links);
.as-console-wrapper {min-height: 100%!important; top: 0}
Note that this method will mutate the links array.
In the project there is an array of objects used for populating the breadcrumb:
export const BREADCRUMBS_LIST = [
{ label: 'Home', path: '/', active: false },
{ label: 'Account', path: '/accounts', active: false },
{ label: 'This Account', path: '/accounts', active: true }
];
it is used to populate the list in the Breadcrumbs component:
import { BREADCRUMBS_LIST } from './...'
...
<Breadcrumbs list={BREADCRUMBS_LIST} />
Everything works fine.
The problem appears when we need to translate those labels based on the user's language. For this, we are using react-intl.
So, I transformed the original array into a component of this form:
import { useIntl } from 'react-intl';
export const BreadcrumbsList = () => {
const intl = useIntl();
return [
{ label: intl.formatMessage({ id: 'Home' }), path: '/', active: false },
{
label: intl.formatMessage({ id: 'Account' }),
path: '/accounts',
active: false
},
{
label: intl.formatMessage({ id: 'This Account' }),
path: '/accounts',
active: true
}
];
};
and use it like this:
<Breadcrumbs list={BreadcrumbsList} />
it seems to be wrong because it returns an error saying:
Cannot read property 'map' of undefined.
In that component, the list was used with map: {list.map(({path, label, active}, index) => {...})
Any ideas how to solve this problem?
Your BreadcrumbsList is actually a custom hook, in order to stick with the Rules of Hooks you need to call it on component's level:
// Add "use" prefix as its a custom hook
const useBreadcrumbsList = () => {
const intl = useIntl();
return [
{ label: intl.formatMessage({ id: "Home" }), path: "/", active: false },
{
label: intl.formatMessage({ id: "Account" }),
path: "/accounts",
active: false,
},
{
label: intl.formatMessage({ id: "This Account" }),
path: "/accounts",
active: true,
},
];
};
// Usage
const Component = () => {
const breadcrumbsList = useBreadcrumbsList();
return <Breadcrumbs list={breadcrumbsList} />;
};
I'm trying to create an AdditionalPage and add it inside the navbar.
I cannot find out how to make it happen:
This is my plugin:
async additionalPages() {
...
errorsPages.push({
path: '/errors',
content: '## Errors',
frontmatter: {
sidebar: true,
},
})
return errorsPages
When I only do this, of course no sidebar are present. This is because I did not add my /errors page inside the navbar.
When I try to add it to
config.js in the sidebar option:
'sidebar': {
'/references/': [
{
title: '📒 API References',
path: '/references/',
collapsable: false,
children: [
'/references/indexes',
'/references/keys',
{
title: 'Errors',
path: '/errors/',
collapsable: false,
},
]
}
]
}
Then the build failes.
Any idea how to add my additionalpages inside my sidebar ?
For add groups in sidebar, try this code:
sidebar: {
'/guide/': [
{
title: 'Group Guide',
children: ['', 'introduction-guide', 'details-guide'],
},
],
'/api/': [
{
title: 'Group API',
children: ['', 'introduction-api', 'details-api'],
},
],
},
The introduction-guide and details-guide are files presents in guide directory.
The introduction-api and details-api are files presents in api directory.
For more details: https://vuepress.vuejs.org/theme/default-theme-config.html#sidebar-groups
i'm doing a project with VueJS, Lodash and Babel, and my problem is that i need to populate a sidebar in my app with routes from a routes.js file. Problem is i don't really know how i can retrieve the data i need.
export default [
{
path: "/admin/login",
name: "admin.login",
component: Login
},
{
path: "/admin",
component: MasterLayout,
meta: { auth: true },
children: [
{
path: "/admin/dashboard",
alias: "/",
menu: { text: "", icon: ""},
name: "admin.dashboard",
component: DashboardIndex
}
]
},
{
path: '/403',
name: 'error.forbidden',
component: ErrorForbidden
},
{
path: "*",
name: 'error.notfound',
component: ErrorPageNotFound
}
];
That is my routes file, i basically need to iterate over each route, check if it has children or a menu property, if it has a menu it gets added to my list of sidebar items, and it's children get added as well with their own menu properties being withdrawn
In the end i'd like to get something like the following
sidebarItems: [
{
name: 'Dashboard',
icon: 'dashboard',
name: 'admin.dashboard'
},
{
name: 'OtherRoute',
icon: 'other-route',
name: 'admin.other-route',
children: [
{
name: 'SubRoute',
icon: 'sub-route',
name: 'admin.sub'
}
]
}
]
This should only account for the routes that contain a menu property.
You basically need iterate recursive over the array, so please try this:
function iterateArrayRoutes(routeArray) {
if(routeArray instanceof Array) {
var routeFormatted = routeArray.reduce(function(last, route){
if (route.hasOwnProperty("menu")) {
var item = {
name: route.name,
icon: route.menu.icon
};
if(route.hasOwnProperty("children") && route.children.length > 0) {
item.children = iterateArrayRoutes(route.children);
}
last.push(item);
}
return last;
}, []);
return routeFormatted;
}
};
Here you have a Demo https://jsfiddle.net/vm1hrwLL/