mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'extensions-api' into enable-extensions-on-main
This commit is contained in:
commit
f97fbdf9ed
24
extensions/example-extension/index.tsx
Normal file
24
extensions/example-extension/index.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { DynamicPageType, LensRendererExtension, PageStore } from "@lens/ui-extensions";
|
||||||
|
import { examplePage, ExtensionIcon } from "./page"
|
||||||
|
|
||||||
|
export default class ExampleExtension extends LensRendererExtension {
|
||||||
|
onActivate() {
|
||||||
|
console.log('EXAMPLE EXTENSION RENDERER: ACTIVATED', this.getMeta());
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPages(pageStore: PageStore) {
|
||||||
|
this.registerPage(pageStore, {
|
||||||
|
type: DynamicPageType.CLUSTER,
|
||||||
|
path: "/extension-example",
|
||||||
|
title: "Example Extension",
|
||||||
|
components: {
|
||||||
|
Page: examplePage(this),
|
||||||
|
MenuIcon: ExtensionIcon,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeactivate() {
|
||||||
|
console.log('EXAMPLE EXTENSION RENDERER: DEACTIVATED', this.getMeta());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,14 @@
|
|||||||
import { LensExtension } from "@lens/extensions";
|
import { LensMainExtension } from "@lens/extensions";
|
||||||
|
|
||||||
export default class ExampleExtensionMain extends LensExtension {
|
export default class ExampleExtensionMain extends LensMainExtension {
|
||||||
onActivate() {
|
onActivate() {
|
||||||
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.getMeta());
|
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.getMeta());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEvent(evt: any) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
onDeactivate() {
|
onDeactivate() {
|
||||||
console.log('EXAMPLE EXTENSION MAIN: DEACTIVATED', this.getMeta());
|
console.log('EXAMPLE EXTENSION MAIN: DEACTIVATED', this.getMeta());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export default class ExampleExtension extends LensRendererExtension {
|
|||||||
this.registerPage(pageStore, {
|
this.registerPage(pageStore, {
|
||||||
type: DynamicPageType.CLUSTER,
|
type: DynamicPageType.CLUSTER,
|
||||||
path: "/extension-example",
|
path: "/extension-example",
|
||||||
menuTitle: "Example Extension",
|
title: "Example Extension",
|
||||||
components: {
|
components: {
|
||||||
Page: examplePage(this),
|
Page: examplePage(this),
|
||||||
MenuIcon: ExtensionIcon,
|
MenuIcon: ExtensionIcon,
|
||||||
|
|||||||
15
src/extensions/dynamic-page.tsx
Normal file
15
src/extensions/dynamic-page.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { cssNames } from "../renderer/utils";
|
||||||
|
import { TabLayout } from "../renderer/components/layout/tab-layout";
|
||||||
|
import { PageRegistration } from "./page-store"
|
||||||
|
|
||||||
|
export class DynamicPage extends React.Component<{ page: PageRegistration }> {
|
||||||
|
render() {
|
||||||
|
const { className, components: { Page }, subPages = [] } = this.props.page;
|
||||||
|
return (
|
||||||
|
<TabLayout className={cssNames("ExtensionPage", className)} tabs={subPages}>
|
||||||
|
<Page/>
|
||||||
|
</TabLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
// Lens-extensions api developer's kit
|
// Lens-extensions api developer's kit
|
||||||
export type { LensExtensionRuntimeEnv, PageStore } from "./lens-runtime";
|
export type { LensExtensionRuntimeEnv, PageStore } from "./lens-renderer-runtime";
|
||||||
|
|
||||||
// APIs
|
// APIs
|
||||||
export * from "./lens-renderer-extension"
|
export * from "./lens-renderer-extension"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { PageStore } from "./lens-runtime"
|
import type { PageStore } from "./lens-renderer-runtime"
|
||||||
import type { PageRegistration } from "./page-store"
|
import type { PageRegistration } from "./page-store"
|
||||||
import { LensExtension } from "./lens-extension"
|
import { LensExtension } from "./lens-extension"
|
||||||
|
|
||||||
|
|||||||
21
src/extensions/lens-renderer-runtime.ts
Normal file
21
src/extensions/lens-renderer-runtime.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Lens extension runtime params available to renderer extensions after activation
|
||||||
|
|
||||||
|
import logger from "../main/logger";
|
||||||
|
import { navigate } from "../renderer/navigation";
|
||||||
|
import { PageRegistration } from "./page-store";
|
||||||
|
|
||||||
|
export interface PageStore {
|
||||||
|
register(params: PageRegistration): () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LensExtensionRuntimeEnv {
|
||||||
|
logger: typeof logger;
|
||||||
|
navigate: typeof navigate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLensRuntime(): LensExtensionRuntimeEnv {
|
||||||
|
return {
|
||||||
|
logger,
|
||||||
|
navigate
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,6 @@
|
|||||||
// Lens extension runtime params available to extensions after activation
|
// Lens extension runtime params available to extensions after activation
|
||||||
|
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
import { PageRegistration } from "./page-store";
|
|
||||||
|
|
||||||
export interface PageStore {
|
|
||||||
register(params: PageRegistration): () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LensExtensionRuntimeEnv {
|
export interface LensExtensionRuntimeEnv {
|
||||||
logger: typeof logger;
|
logger: typeof logger;
|
||||||
@ -13,6 +8,6 @@ export interface LensExtensionRuntimeEnv {
|
|||||||
|
|
||||||
export function getLensRuntime(): LensExtensionRuntimeEnv {
|
export function getLensRuntime(): LensExtensionRuntimeEnv {
|
||||||
return {
|
return {
|
||||||
logger,
|
logger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,18 +2,24 @@
|
|||||||
|
|
||||||
import { computed, observable } from "mobx";
|
import { computed, observable } from "mobx";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { IconProps } from "../renderer/components/icon";
|
import { RouteProps } from "react-router";
|
||||||
|
import { IconProps } from "../renderer/components/icon";
|
||||||
|
import { IClassName } from "../renderer/utils";
|
||||||
|
import { TabRoute } from "../renderer/components/layout/tab-layout";
|
||||||
|
|
||||||
export enum DynamicPageType {
|
export enum DynamicPageType {
|
||||||
GLOBAL = "lens-scope",
|
GLOBAL = "lens-scope",
|
||||||
CLUSTER = "cluster-view-scope",
|
CLUSTER = "cluster-view-scope",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageRegistration {
|
export interface PageRegistration extends RouteProps {
|
||||||
|
className?: IClassName;
|
||||||
|
url?: string; // initial url to be used for building menus and tabs, otherwise "path" applied by default
|
||||||
path: string; // route-path
|
path: string; // route-path
|
||||||
menuTitle: string;
|
title: React.ReactNode; // used in sidebar's & tabs-layout
|
||||||
type: DynamicPageType;
|
type: DynamicPageType;
|
||||||
components: PageComponents;
|
components: PageComponents;
|
||||||
|
subPages?: (PageRegistration & TabRoute)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageComponents {
|
export interface PageComponents {
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import logger from "../../main/logger";
|
|||||||
import { clusterIpc } from "../../common/cluster-ipc";
|
import { clusterIpc } from "../../common/cluster-ipc";
|
||||||
import { webFrame } from "electron";
|
import { webFrame } from "electron";
|
||||||
import { pageStore } from "../../extensions/page-store";
|
import { pageStore } from "../../extensions/page-store";
|
||||||
|
import { DynamicPage } from "../../extensions/dynamic-page";
|
||||||
import { extensionLoader } from "../../extensions/extension-loader";
|
import { extensionLoader } from "../../extensions/extension-loader";
|
||||||
import { getLensRuntime } from "../../extensions/lens-runtime";
|
import { getLensRuntime } from "../../extensions/lens-runtime";
|
||||||
|
|
||||||
@ -77,8 +78,8 @@ 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}/>
|
||||||
{pageStore.clusterPages.map(({ path, components: { Page } }) => {
|
{pageStore.clusterPages.map(page => {
|
||||||
return <Route key={path} path={path} component={Page}/>
|
return <Route {...page} key={page.path} render={() => <DynamicPage page={page}/>}/>
|
||||||
})}
|
})}
|
||||||
<Redirect exact from="/" to={this.startURL}/>
|
<Redirect exact from="/" to={this.startURL}/>
|
||||||
<Route component={NotFound}/>
|
<Route component={NotFound}/>
|
||||||
|
|||||||
@ -22,7 +22,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 { DragDropContext, Droppable, Draggable, DropResult, DroppableProvided, DraggableProvided } from "react-beautiful-dnd";
|
import { DragDropContext, Droppable, Draggable, DropResult, DroppableProvided, DraggableProvided } from "react-beautiful-dnd";
|
||||||
import { dynamicPages } from "../../../extensions/register-page";
|
import { pageStore } from "../../../extensions/page-store";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: IClassName;
|
className?: IClassName;
|
||||||
@ -156,7 +156,7 @@ export class ClustersMenu extends React.Component<Props> {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="dynamic-pages">
|
<div className="dynamic-pages">
|
||||||
{dynamicPages.globalPages.map(({ path, components: { MenuIcon } }) => {
|
{pageStore.globalPages.map(({ path, components: { MenuIcon } }) => {
|
||||||
return <MenuIcon key={path} onClick={() => navigate(path)}/>
|
return <MenuIcon key={path} onClick={() => navigate(path)}/>
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -28,7 +28,7 @@ import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resourc
|
|||||||
import { CustomResources } from "../+custom-resources/custom-resources";
|
import { CustomResources } from "../+custom-resources/custom-resources";
|
||||||
import { navigation } from "../../navigation";
|
import { navigation } from "../../navigation";
|
||||||
import { isAllowedResource } from "../../../common/rbac"
|
import { isAllowedResource } from "../../../common/rbac"
|
||||||
import { dynamicPages } from "../../../extensions/register-page";
|
import { pageStore } from "../../../extensions/page-store";
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
||||||
type SidebarContextValue = {
|
type SidebarContextValue = {
|
||||||
@ -184,14 +184,14 @@ export class Sidebar extends React.Component<Props> {
|
|||||||
>
|
>
|
||||||
{this.renderCustomResources()}
|
{this.renderCustomResources()}
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
{dynamicPages.clusterPages.map(({ path, menuTitle, components: { MenuIcon } }) => {
|
{pageStore.clusterPages.map(({ path, title, components: { MenuIcon } }) => {
|
||||||
return (
|
return (
|
||||||
<SidebarNavItem
|
<SidebarNavItem
|
||||||
key={path}
|
key={path}
|
||||||
id={`extension-${path}`}
|
id={`extension-${path}`}
|
||||||
url={path}
|
url={path}
|
||||||
routePath={path}
|
routePath={path}
|
||||||
text={menuTitle}
|
text={title}
|
||||||
icon={<MenuIcon/>}
|
icon={<MenuIcon/>}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import "./tab-layout.scss";
|
import "./tab-layout.scss";
|
||||||
|
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { matchPath, RouteProps } from "react-router-dom";
|
import { matchPath, RouteProps } from "react-router-dom";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
@ -7,7 +6,6 @@ import { cssNames } from "../../utils";
|
|||||||
import { Tab, Tabs } from "../tabs";
|
import { Tab, Tabs } from "../tabs";
|
||||||
import { ErrorBoundary } from "../error-boundary";
|
import { ErrorBoundary } from "../error-boundary";
|
||||||
import { navigate, navigation } from "../../navigation";
|
import { navigate, navigation } from "../../navigation";
|
||||||
import { getHostedCluster } from "../../../common/cluster-store";
|
|
||||||
|
|
||||||
export interface TabRoute extends RouteProps {
|
export interface TabRoute extends RouteProps {
|
||||||
title: React.ReactNode;
|
title: React.ReactNode;
|
||||||
@ -23,17 +21,13 @@ interface Props {
|
|||||||
|
|
||||||
export const TabLayout = observer(({ className, contentClass, tabs, children }: Props) => {
|
export const TabLayout = observer(({ className, contentClass, tabs, children }: Props) => {
|
||||||
const routePath = navigation.location.pathname;
|
const routePath = navigation.location.pathname;
|
||||||
const cluster = getHostedCluster();
|
|
||||||
if (!cluster) {
|
|
||||||
return null; // fix: skip render when removing active (visible) cluster
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className={cssNames("TabLayout", className)}>
|
<div className={cssNames("TabLayout", className)}>
|
||||||
{tabs && (
|
{tabs && (
|
||||||
<Tabs center onChange={(url) => navigate(url)}>
|
<Tabs center onChange={(url) => navigate(url)}>
|
||||||
{tabs.map(({ title, path, url, ...routeProps }) => {
|
{tabs.map(({ title, path, url, ...routeProps }) => {
|
||||||
const isActive = !!matchPath(routePath, { path, ...routeProps });
|
const isActive = !!matchPath(routePath, { path, ...routeProps });
|
||||||
return <Tab key={url} label={title} value={url} active={isActive} />;
|
return <Tab key={url} label={title} value={url} active={isActive}/>;
|
||||||
})}
|
})}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user