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:
parent
d3979c2883
commit
d07e199976
@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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">>();
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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}/>
|
||||||
|
|||||||
@ -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))}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user