mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Allow extension cluster menus to have a parent (#1452)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
c94c599cdd
commit
d7cba7d4d6
@ -1,9 +1,10 @@
|
|||||||
// Extensions-api -> Register page menu items
|
// Extensions-api -> Register page menu items
|
||||||
import type { IconProps } from "../../renderer/components/icon";
|
import type { IconProps } from "../../renderer/components/icon";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { action } from "mobx";
|
import { action, computed } from "mobx";
|
||||||
import { BaseRegistry } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
import { LensExtension } from "../lens-extension";
|
import { LensExtension } from "../lens-extension";
|
||||||
|
import { RegisteredPage } from "./page-registry";
|
||||||
|
|
||||||
export interface PageMenuTarget<P extends object = any> {
|
export interface PageMenuTarget<P extends object = any> {
|
||||||
extensionId?: string;
|
extensionId?: string;
|
||||||
@ -17,11 +18,16 @@ export interface PageMenuRegistration {
|
|||||||
components: PageMenuComponents;
|
components: PageMenuComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ClusterPageMenuRegistration extends PageMenuRegistration {
|
||||||
|
id?: string;
|
||||||
|
parentId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PageMenuComponents {
|
export interface PageMenuComponents {
|
||||||
Icon: React.ComponentType<IconProps>;
|
Icon: React.ComponentType<IconProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PageMenuRegistry extends BaseRegistry<PageMenuRegistration, Required<PageMenuRegistration>> {
|
export class GlobalPageMenuRegistry extends BaseRegistry<PageMenuRegistration> {
|
||||||
@action
|
@action
|
||||||
add(items: PageMenuRegistration[], ext: LensExtension) {
|
add(items: PageMenuRegistration[], ext: LensExtension) {
|
||||||
const normalizedItems = items.map(menuItem => {
|
const normalizedItems = items.map(menuItem => {
|
||||||
@ -35,5 +41,31 @@ export class PageMenuRegistry extends BaseRegistry<PageMenuRegistration, Require
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const globalPageMenuRegistry = new PageMenuRegistry();
|
export class ClusterPageMenuRegistry extends BaseRegistry<ClusterPageMenuRegistration> {
|
||||||
export const clusterPageMenuRegistry = new PageMenuRegistry();
|
@action
|
||||||
|
add(items: PageMenuRegistration[], ext: LensExtension) {
|
||||||
|
const normalizedItems = items.map(menuItem => {
|
||||||
|
menuItem.target = {
|
||||||
|
extensionId: ext.name,
|
||||||
|
...(menuItem.target || {}),
|
||||||
|
};
|
||||||
|
return menuItem;
|
||||||
|
});
|
||||||
|
return super.add(normalizedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRootItems() {
|
||||||
|
return this.getItems().filter((item) => !item.parentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubItems(parent: ClusterPageMenuRegistration) {
|
||||||
|
return this.getItems().filter((item) => item.parentId === parent.id && item.target.extensionId === parent.target.extensionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByPage(page: RegisteredPage) {
|
||||||
|
return this.getItems().find((item) => item.target?.pageId == page.id && item.target?.extensionId === page.extensionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const globalPageMenuRegistry = new GlobalPageMenuRegistry();
|
||||||
|
export const clusterPageMenuRegistry = new ClusterPageMenuRegistry();
|
||||||
|
|||||||
@ -34,12 +34,15 @@ import { Terminal } from "./dock/terminal";
|
|||||||
import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store";
|
import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store";
|
||||||
import logger from "../../main/logger";
|
import logger from "../../main/logger";
|
||||||
import { webFrame } from "electron";
|
import { webFrame } from "electron";
|
||||||
import { clusterPageRegistry } from "../../extensions/registries/page-registry";
|
import { clusterPageRegistry, getExtensionPageUrl, PageRegistration, RegisteredPage } from "../../extensions/registries/page-registry";
|
||||||
import { extensionLoader } from "../../extensions/extension-loader";
|
import { extensionLoader } from "../../extensions/extension-loader";
|
||||||
import { appEventBus } from "../../common/event-bus";
|
import { appEventBus } from "../../common/event-bus";
|
||||||
import { requestMain } from "../../common/ipc";
|
import { requestMain } from "../../common/ipc";
|
||||||
import whatInput from 'what-input';
|
import whatInput from 'what-input';
|
||||||
import { clusterSetFrameIdHandler } from "../../common/cluster-ipc";
|
import { clusterSetFrameIdHandler } from "../../common/cluster-ipc";
|
||||||
|
import { ClusterPageMenuRegistration, clusterPageMenuRegistry } from "../../extensions/registries";
|
||||||
|
import { TabLayoutRoute, TabLayout } from "./layout/tab-layout";
|
||||||
|
import { Trans } from "@lingui/macro";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class App extends React.Component {
|
export class App extends React.Component {
|
||||||
@ -72,9 +75,48 @@ export class App extends React.Component {
|
|||||||
return workloadsURL();
|
return workloadsURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTabLayoutRoutes(menuItem: ClusterPageMenuRegistration) {
|
||||||
|
const routes: TabLayoutRoute[] = [];
|
||||||
|
if (!menuItem.id) {
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
clusterPageMenuRegistry.getSubItems(menuItem).forEach((item) => {
|
||||||
|
const page = clusterPageRegistry.getByPageMenuTarget(item.target);
|
||||||
|
if (page) {
|
||||||
|
routes.push({
|
||||||
|
routePath: page.routePath,
|
||||||
|
url: getExtensionPageUrl({ extensionId: page.extensionId, pageId: page.id, params: item.target.params }),
|
||||||
|
title: item.title,
|
||||||
|
component: page.components.Page,
|
||||||
|
exact: page.exact
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderExtensionTabLayoutRoutes() {
|
||||||
|
return clusterPageMenuRegistry.getRootItems().map((menu, index) => {
|
||||||
|
const tabRoutes = this.getTabLayoutRoutes(menu);
|
||||||
|
if (tabRoutes.length > 0) {
|
||||||
|
const pageComponent = () => <TabLayout tabs={tabRoutes} />;
|
||||||
|
return <Route key={"extension-tab-layout-route-" + index} component={pageComponent}/>;
|
||||||
|
} else {
|
||||||
|
const page = clusterPageRegistry.getByPageMenuTarget(menu.target);
|
||||||
|
if (page) {
|
||||||
|
const pageComponent = () => <page.components.Page />;
|
||||||
|
return <Route key={"extension-tab-layout-route-" + index} path={page.routePath} exact={page.exact} component={pageComponent}/>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
renderExtensionRoutes() {
|
renderExtensionRoutes() {
|
||||||
return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath }) => {
|
return clusterPageRegistry.getItems().map((page, index) => {
|
||||||
return <Route key={routePath} path={routePath} exact={exact} component={Page}/>;
|
const menu = clusterPageMenuRegistry.getByPage(page);
|
||||||
|
if (!menu) {
|
||||||
|
return <Route key={"extension-route-" + index} path={page.routePath} exact={page.exact} component={page.components.Page}/>;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +138,7 @@ export class App extends React.Component {
|
|||||||
<Route component={CustomResources} {...crdRoute}/>
|
<Route component={CustomResources} {...crdRoute}/>
|
||||||
<Route component={UserManagement} {...usersManagementRoute}/>
|
<Route component={UserManagement} {...usersManagementRoute}/>
|
||||||
<Route component={Apps} {...appsRoute}/>
|
<Route component={Apps} {...appsRoute}/>
|
||||||
|
{this.renderExtensionTabLayoutRoutes()}
|
||||||
{this.renderExtensionRoutes()}
|
{this.renderExtensionRoutes()}
|
||||||
<Redirect exact from="/" to={this.startURL}/>
|
<Redirect exact from="/" to={this.startURL}/>
|
||||||
<Route component={NotFound}/>
|
<Route component={NotFound}/>
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import { CustomResources } from "../+custom-resources/custom-resources";
|
|||||||
import { isActiveRoute, navigation } from "../../navigation";
|
import { isActiveRoute, navigation } from "../../navigation";
|
||||||
import { isAllowedResource } from "../../../common/rbac";
|
import { isAllowedResource } from "../../../common/rbac";
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import { clusterPageMenuRegistry, clusterPageRegistry, getExtensionPageUrl } from "../../../extensions/registries";
|
import { ClusterPageMenuRegistration, clusterPageMenuRegistry, clusterPageRegistry, getExtensionPageUrl, RegisteredPage } from "../../../extensions/registries";
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
||||||
type SidebarContextValue = {
|
type SidebarContextValue = {
|
||||||
@ -76,6 +76,52 @@ export class Sidebar extends React.Component<Props> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTabLayoutRoutes(menu: ClusterPageMenuRegistration): TabLayoutRoute[] {
|
||||||
|
if (!menu.id) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const routes: TabLayoutRoute[] = [];
|
||||||
|
|
||||||
|
clusterPageMenuRegistry.getSubItems(menu).forEach((subItem) => {
|
||||||
|
const subPage = clusterPageRegistry.getByPageMenuTarget(subItem.target);
|
||||||
|
if (subPage) {
|
||||||
|
routes.push({
|
||||||
|
routePath: subPage.routePath,
|
||||||
|
url: getExtensionPageUrl({ extensionId: subPage.extensionId, pageId: subPage.id, params: subItem.target.params }),
|
||||||
|
title: subItem.title,
|
||||||
|
component: subPage.components.Page,
|
||||||
|
exact: subPage.exact
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRegisteredMenus() {
|
||||||
|
return clusterPageMenuRegistry.getRootItems().map((menuItem) => {
|
||||||
|
const registeredPage = clusterPageRegistry.getByPageMenuTarget(menuItem.target);
|
||||||
|
let pageUrl: string;
|
||||||
|
let isActive = false;
|
||||||
|
if (registeredPage) {
|
||||||
|
const { extensionId, id: pageId } = registeredPage;
|
||||||
|
pageUrl = getExtensionPageUrl({ extensionId, pageId, params: menuItem.target.params });
|
||||||
|
isActive = pageUrl === navigation.location.pathname;
|
||||||
|
}
|
||||||
|
const tabRoutes = this.getTabLayoutRoutes(menuItem);
|
||||||
|
if (!registeredPage && tabRoutes.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<SidebarNavItem
|
||||||
|
key={pageUrl} url={pageUrl}
|
||||||
|
text={menuItem.title} icon={<menuItem.components.Icon/>}
|
||||||
|
isActive={isActive}
|
||||||
|
subMenus={tabRoutes}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { toggle, isPinned, className } = this.props;
|
const { toggle, isPinned, className } = this.props;
|
||||||
const query = namespaceStore.getContextParams();
|
const query = namespaceStore.getContextParams();
|
||||||
@ -191,20 +237,7 @@ export class Sidebar extends React.Component<Props> {
|
|||||||
>
|
>
|
||||||
{this.renderCustomResources()}
|
{this.renderCustomResources()}
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
{clusterPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
{this.renderRegisteredMenus()}
|
||||||
const registeredPage = clusterPageRegistry.getByPageMenuTarget(target);
|
|
||||||
if (!registeredPage) return;
|
|
||||||
const { extensionId, id: pageId } = registeredPage;
|
|
||||||
const pageUrl = getExtensionPageUrl({ extensionId, pageId, params: target.params });
|
|
||||||
const isActive = pageUrl === navigation.location.pathname;
|
|
||||||
return (
|
|
||||||
<SidebarNavItem
|
|
||||||
key={pageUrl} url={pageUrl}
|
|
||||||
text={title} icon={<Icon/>}
|
|
||||||
isActive={isActive}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SidebarContext.Provider>
|
</SidebarContext.Provider>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user