mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Also solve composed typing of application menu by using Discriminated Unions of TypeScript, see: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
117 lines
3.3 KiB
TypeScript
117 lines
3.3 KiB
TypeScript
/**
|
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
*/
|
|
import { getInjectable } from "@ogre-tools/injectable";
|
|
import { Menu } from "electron";
|
|
import type { MenuItemOpts } from "./application-menu-items.injectable";
|
|
import type { Composite } from "./menu-items/get-composite/get-composite";
|
|
import type { ApplicationMenuItemTypes } from "./menu-items/application-menu-item-injection-token";
|
|
import { pipeline } from "@ogre-tools/fp";
|
|
import { map, sortBy } from "lodash/fp";
|
|
import type { MenuItemRoot } from "./application-menu-item-composite.injectable";
|
|
|
|
const populateApplicationMenuInjectable = getInjectable({
|
|
id: "populate-application-menu",
|
|
|
|
instantiate: () => (composite: Composite<ApplicationMenuItemTypes | MenuItemRoot>) => {
|
|
const topLevelMenus = composite.children.filter(
|
|
(x): x is Composite<ApplicationMenuItemTypes> => x.value.kind !== "root",
|
|
);
|
|
|
|
const electronTemplate = topLevelMenus.map(toHierarchicalElectronMenuItem);
|
|
|
|
Menu.setApplicationMenu(Menu.buildFromTemplate(electronTemplate));
|
|
},
|
|
|
|
causesSideEffects: true,
|
|
});
|
|
|
|
export default populateApplicationMenuInjectable;
|
|
|
|
const toHierarchicalElectronMenuItem = (
|
|
composite: Composite<ApplicationMenuItemTypes>,
|
|
): MenuItemOpts => {
|
|
switch (composite.value.kind) {
|
|
case "top-level-menu": {
|
|
const {
|
|
id,
|
|
value: { label, role },
|
|
} = composite;
|
|
|
|
return {
|
|
...(id ? { id } : {}),
|
|
...(role ? { role } : {}),
|
|
label,
|
|
|
|
submenu: pipeline(
|
|
composite.children,
|
|
sortBy((childComposite) => childComposite.value.orderNumber),
|
|
map(toHierarchicalElectronMenuItem),
|
|
),
|
|
};
|
|
}
|
|
|
|
case "sub-menu": {
|
|
const {
|
|
id,
|
|
value: { label },
|
|
} = composite;
|
|
|
|
return {
|
|
...(id ? { id } : {}),
|
|
label,
|
|
|
|
submenu: pipeline(
|
|
composite.children,
|
|
sortBy((childComposite) => childComposite.value.orderNumber),
|
|
map(toHierarchicalElectronMenuItem),
|
|
),
|
|
};
|
|
}
|
|
|
|
case "clickable-menu-item": {
|
|
const {
|
|
id,
|
|
value: { label, onClick, keyboardShortcut },
|
|
} = composite;
|
|
|
|
return {
|
|
...(id ? { id } : {}),
|
|
...(label ? { label } : {}),
|
|
...(keyboardShortcut ? { accelerator: keyboardShortcut }: {}),
|
|
click: onClick,
|
|
};
|
|
}
|
|
|
|
case "os-action-menu-item": {
|
|
const {
|
|
value: { label, keyboardShortcut, actionName },
|
|
} = composite;
|
|
|
|
return {
|
|
...(label ? { label } : {}),
|
|
...(keyboardShortcut ? { accelerator: keyboardShortcut } : {}),
|
|
role: actionName,
|
|
};
|
|
}
|
|
|
|
case "separator": {
|
|
return {
|
|
type: "separator",
|
|
};
|
|
}
|
|
|
|
default: {
|
|
// Note: this will fail at transpilation time, if all ApplicationMenuItemTypes
|
|
// are not handled in switch/case.
|
|
const _exhaustiveCheck: never = composite.value;
|
|
|
|
// Note: this code is unreachable, it is here to make ts not complain about
|
|
// _exhaustiveCheck not being used.
|
|
// See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking
|
|
throw new Error(`Tried to create application menu, but foreign menu item was encountered: ${_exhaustiveCheck} ${composite.value}`);
|
|
}
|
|
}
|
|
};
|