1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

introduce PageMenuTarget

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen 2020-11-13 14:36:18 +02:00
parent d3979c2883
commit d07e199976
9 changed files with 30 additions and 40 deletions

View File

@ -5,6 +5,8 @@ import { SupportPage } from "./src/support";
export default class SupportPageRendererExtension extends LensRendererExtension { export default class SupportPageRendererExtension extends LensRendererExtension {
globalPages: Interface.PageRegistration[] = [ globalPages: Interface.PageRegistration[] = [
{ {
id: "support",
routePath: "/support",
components: { components: {
Page: SupportPage, Page: SupportPage,
} }
@ -14,7 +16,7 @@ export default class SupportPageRendererExtension extends LensRendererExtension
statusBarItems: Interface.StatusBarRegistration[] = [ statusBarItems: Interface.StatusBarRegistration[] = [
{ {
item: ( item: (
<div className="SupportPageIcon flex align-center" onClick={() => this.navigate()}> <div className="SupportPageIcon flex align-center" onClick={() => this.navigate("/support")}>
<Component.Icon interactive material="help" smallest/> <Component.Icon interactive material="help" smallest/>
</div> </div>
) )

View File

@ -14,7 +14,6 @@ export interface LensExtensionManifest {
} }
export class LensExtension { export class LensExtension {
readonly routePrefix = "/extension/:name"
readonly manifest: LensExtensionManifest; readonly manifest: LensExtensionManifest;
readonly manifestPath: string; readonly manifestPath: string;
readonly isBundled: boolean; readonly isBundled: boolean;

View File

@ -2,13 +2,14 @@ import type { MenuRegistration } from "./registries/menu-registry";
import { observable } from "mobx"; import { observable } from "mobx";
import { LensExtension } from "./lens-extension" import { LensExtension } from "./lens-extension"
import { WindowManager } from "../main/window-manager"; import { WindowManager } from "../main/window-manager";
import { getPageUrl } from "./registries/page-registry"
export class LensMainExtension extends LensExtension { export class LensMainExtension extends LensExtension {
@observable.shallow appMenus: MenuRegistration[] = [] @observable.shallow appMenus: MenuRegistration[] = []
async navigate(location?: string, frameId?: number) { async navigate(location?: string, frameId?: number) {
const windowManager = WindowManager.getInstance<WindowManager>(); const windowManager = WindowManager.getInstance<WindowManager>();
const url = this.getPageUrl(location); // get full path to extension's page const url = getPageUrl(this, location); // get full path to extension's page
await windowManager.navigate(url, frameId); await windowManager.navigate(url, frameId);
} }
} }

View File

@ -1,6 +1,7 @@
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries" import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
import { observable } from "mobx"; import { observable } from "mobx";
import { LensExtension } from "./lens-extension" import { LensExtension } from "./lens-extension"
import { getPageUrl } from "./registries/page-registry"
export class LensRendererExtension extends LensExtension { export class LensRendererExtension extends LensExtension {
@observable.shallow globalPages: PageRegistration[] = [] @observable.shallow globalPages: PageRegistration[] = []
@ -16,6 +17,6 @@ export class LensRendererExtension extends LensExtension {
async navigate(location?: string) { async navigate(location?: string) {
const { navigate } = await import("../renderer/navigation"); const { navigate } = await import("../renderer/navigation");
navigate(this.getPageUrl(location)); navigate(getPageUrl(this, location));
} }
} }

View File

@ -5,13 +5,18 @@ import { action } from "mobx";
import type { IconProps } from "../../renderer/components/icon"; import type { IconProps } from "../../renderer/components/icon";
import { BaseRegistry } from "./base-registry"; import { BaseRegistry } from "./base-registry";
import { LensExtension } from "../lens-extension"; import { LensExtension } from "../lens-extension";
import { getPageUrl } from "./page-registry"; import { PageRegistration } from "../interfaces";
export interface PageMenuTarget {
pageId: string;
extensionId: string;
params: object;
}
export interface PageMenuRegistration { export interface PageMenuRegistration {
url?: string; // when not provided initial extension's path used, e.g. "/extension/lens-extension-name" target?: PageMenuTarget;
title: React.ReactNode; title: React.ReactNode;
components: PageMenuComponents; components: PageMenuComponents;
subMenus?: PageSubMenuRegistration[];
} }
export interface PageSubMenuRegistration { export interface PageSubMenuRegistration {
@ -24,18 +29,15 @@ export interface PageMenuComponents {
} }
export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> { export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> {
@action @action
add(items: T[], ext?: LensExtension) { add(items: T[], ext?: LensExtension) {
const normalizedItems = items.map((i) => { const normalizedItems = items.map((i) => {
i.url = getPageUrl(ext, i.url) i.target.extensionId = ext.name
return i return i
}) })
return super.add(normalizedItems); return super.add(normalizedItems);
} }
getByRoutePath(routePath: string) {
return this.getItems().find((i) => i.url === routePath)
}
} }
export const globalPageMenuRegistry = new PageMenuRegistry<Omit<PageMenuRegistration, "subMenus">>(); export const globalPageMenuRegistry = new PageMenuRegistry<Omit<PageMenuRegistration, "subMenus">>();

View File

@ -5,12 +5,13 @@ import { action } from "mobx";
import { compile } from "path-to-regexp"; import { compile } from "path-to-regexp";
import { BaseRegistry } from "./base-registry"; import { BaseRegistry } from "./base-registry";
import { LensExtension } from "../lens-extension" import { LensExtension } from "../lens-extension"
import { PageMenuTarget } from "./page-menu-registry";
export interface PageRegistration { export interface PageRegistration {
id: string; // hello-world:id
routePath?: string; // additional (suffix) route path to base extension's route: "/extension/:name" routePath?: string; // additional (suffix) route path to base extension's route: "/extension/:name"
exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool
components: PageComponents; components: PageComponents;
subPages?: SubPageRegistration[];
} }
export interface SubPageRegistration { export interface SubPageRegistration {
@ -41,8 +42,8 @@ export class PageRegistry<T extends PageRegistration> extends BaseRegistry<T> {
return super.add(normalizedItems); return super.add(normalizedItems);
} }
getByUrl(url: string) { getByPageMenuTarget(target: PageMenuTarget) {
return this.getItems().find((i) => i.routePath === url) return this.getItems().find((page) => page.routePath.startsWith(`/extension/${target.extensionId}/`) && page.id === target.pageId)
} }
} }

View File

@ -74,27 +74,8 @@ export class App extends React.Component {
} }
renderExtensionRoutes() { renderExtensionRoutes() {
return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath, subPages }) => { return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath }) => {
const Component = () => { const Component = () => {
if (subPages) {
const tabs: TabLayoutRoute[] = subPages.map(({ exact, routePath, components: { Page } }) => {
const menuItem = clusterPageMenuRegistry.getByRoutePath(routePath);
if (!menuItem) return;
return {
routePath, exact,
component: Page,
url: menuItem.url,
title: menuItem.title,
}
}).filter(Boolean);
if (tabs.length > 0) {
return (
<Page>
<TabLayout tabs={tabs}/>
</Page>
)
}
}
return <Page/> return <Page/>
}; };
return <Route key={routePath} path={routePath} exact={exact} component={Component}/> return <Route key={routePath} path={routePath} exact={exact} component={Component}/>

View File

@ -23,6 +23,7 @@ import { ConfirmDialog } from "../confirm-dialog";
import { clusterIpc } from "../../../common/cluster-ipc"; import { clusterIpc } from "../../../common/cluster-ipc";
import { clusterViewURL } from "./cluster-view.route"; import { clusterViewURL } from "./cluster-view.route";
import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries"; import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
import { compile } from "path-to-regexp";
interface Props { interface Props {
className?: IClassName; className?: IClassName;
@ -148,8 +149,8 @@ export class ClustersMenu extends React.Component<Props> {
)} )}
</div> </div>
<div className="extensions"> <div className="extensions">
{globalPageMenuRegistry.getItems().map(({ title, url, components: { Icon } }) => { {globalPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
const registeredPage = globalPageRegistry.getByUrl(url); const registeredPage = globalPageRegistry.getByPageMenuTarget(target);
if (!registeredPage) return; if (!registeredPage) return;
const { routePath, exact } = registeredPage; const { routePath, exact } = registeredPage;
return ( return (
@ -157,7 +158,7 @@ export class ClustersMenu extends React.Component<Props> {
key={routePath} key={routePath}
tooltip={title} tooltip={title}
active={isActiveRoute({ path: routePath, exact })} active={isActiveRoute({ path: routePath, exact })}
onClick={() => navigate(url)} onClick={() => navigate(compile(routePath)(target.params))}
/> />
) )
})} })}

View File

@ -30,6 +30,7 @@ import { isActiveRoute } from "../../navigation";
import { isAllowedResource } from "../../../common/rbac" import { isAllowedResource } from "../../../common/rbac"
import { Spinner } from "../spinner"; import { Spinner } from "../spinner";
import { clusterPageMenuRegistry, clusterPageRegistry } from "../../../extensions/registries"; import { clusterPageMenuRegistry, clusterPageRegistry } from "../../../extensions/registries";
import { compile } from "path-to-regexp";
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false }); const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
type SidebarContextValue = { type SidebarContextValue = {
@ -191,10 +192,11 @@ export class Sidebar extends React.Component<Props> {
> >
{this.renderCustomResources()} {this.renderCustomResources()}
</SidebarNavItem> </SidebarNavItem>
{clusterPageMenuRegistry.getItems().map(({ title, url, components: { Icon } }) => { {clusterPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
const registeredPage = clusterPageRegistry.getByUrl(url); const registeredPage = clusterPageRegistry.getByPageMenuTarget(target);
if (!registeredPage) return; if (!registeredPage) return;
const { routePath, exact } = registeredPage; const { routePath, exact } = registeredPage;
const url = compile(routePath)(target.params)
return ( return (
<SidebarNavItem <SidebarNavItem
key={url} key={url}