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

example-extension reworks -- part 2

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-09-08 15:20:41 +03:00 committed by Lauri Nevala
parent f5cea39c64
commit 89bc526e4d
10 changed files with 44 additions and 28 deletions

View File

@ -1,4 +1,4 @@
import { Icon, LensExtension } from "@lens/extensions"; // fixme: map to generated types from "extension-api.d.ts"
import { DynamicPageType, Icon, LensExtension } from "@lens/extensions"; // fixme: map to generated types from "extension-api.d.ts"
import React from "react";
import path from "path";
@ -8,10 +8,11 @@ export default class ExampleExtension extends LensExtension {
onActivate() {
console.log('EXAMPLE EXTENSION: ACTIVATE', this.getMeta())
this.unRegisterPage = this.runtime.dynamicPages.register({
type: "cluster-view",
type: DynamicPageType.CLUSTER,
path: "/extension-example",
menuTitle: "Example Extension",
components: {
Main: ExtensionPage,
Page: ExtensionPage,
MenuIcon: ExtensionIcon,
}
})
@ -27,7 +28,7 @@ export function ExtensionIcon(props: {} /*IconProps |*/) {
return <Icon {...props} material="camera" tooltip={path.basename(__filename)}/>
}
// todo: provide extension instance and runtime params (via context or props)
// todo: provide extension-instance and lens-runtime (context/props/extension-js-scope)
export class ExtensionPage extends React.Component {
render() {
return (

View File

@ -3,6 +3,7 @@ export type { LensRuntimeRendererEnv } from "./lens-runtime";
// APIs
export * from "./extension"
export { DynamicPageType } from "./register-page";
// Common UI components
export * from "../renderer/components/icon"

View File

@ -1,18 +1,14 @@
// Lens runtime for injecting to extension on activation
import { apiManager } from "../renderer/api/api-manager";
// Lens renderer runtime params available to the extension after activation
import logger from "../main/logger";
import { dynamicPages } from "../renderer/components/cluster-manager/register-page";
import { dynamicPages } from "./register-page";
export interface LensRuntimeRendererEnv {
apiManager: typeof apiManager;
logger: typeof logger;
dynamicPages: typeof dynamicPages
}
// todo: expose more public runtime apis: stores, managers, etc.
export function getLensRuntime(): LensRuntimeRendererEnv {
return {
apiManager,
logger,
dynamicPages,
}

View File

@ -1,12 +1,18 @@
// Dynamic pages
// Extensions-api -> Dynamic pages
import React from "react";
import { computed, observable } from "mobx";
import type { IconProps } from "../icon";
import React from "react";
import type { IconProps } from "../renderer/components/icon";
export enum DynamicPageType {
GLOBAL = "lens-scope",
CLUSTER = "cluster-view-scope",
}
export interface PageRegistration {
path: string;
type: "global" | "cluster-view";
path: string; // route-path
menuTitle: string;
type: DynamicPageType;
components: PageComponents;
}
@ -19,13 +25,14 @@ export class PagesStore {
protected pages = observable.array<PageRegistration>([], { deep: false });
@computed get globalPages() {
return this.pages.filter(page => page.type === "global");
return this.pages.filter(page => page.type === DynamicPageType.GLOBAL);
}
@computed get clusterPages() {
return this.pages.filter(page => page.type === "cluster-view");
return this.pages.filter(page => page.type === DynamicPageType.CLUSTER);
}
// todo: verify paths to avoid collision with existing pages
register(params: PageRegistration) {
this.pages.push(params);
return () => {

View File

@ -10,6 +10,7 @@ import { i18nStore } from "./i18n";
import { themeStore } from "./theme.store";
import { App } from "./components/app";
import { LensApp } from "./lens-app";
import { getLensRuntime } from "../extensions/lens-runtime";
type AppComponent = React.ComponentType & {
init?(): void;
@ -32,6 +33,7 @@ export async function bootstrap(App: AppComponent) {
// init app's dependencies if any
if (App.init) {
await App.init();
extensionStore.autoEnableOnLoad(getLensRuntime);
}
render(<App/>, rootElem);
}

View File

@ -35,6 +35,7 @@ import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store
import logger from "../../main/logger";
import { clusterIpc } from "../../common/cluster-ipc";
import { webFrame } from "electron";
import { dynamicPages } from "../../extensions/register-page";
@observer
export class App extends React.Component {
@ -71,6 +72,9 @@ export class App extends React.Component {
<Route component={CustomResources} {...crdRoute}/>
<Route component={UserManagement} {...usersManagementRoute}/>
<Route component={Apps} {...appsRoute}/>
{dynamicPages.clusterPages.map(({ path, components: { Page } }) => {
return <Route key={path} path={path} component={Page}/>
})}
<Redirect exact from="/" to={this.startURL}/>
<Route component={NotFound}/>
</Switch>

View File

@ -15,7 +15,7 @@ import { Extensions, extensionsRoute } from "../+extensions";
import { clusterViewRoute, clusterViewURL, getMatchedCluster, getMatchedClusterId } from "./cluster-view.route";
import { clusterStore } from "../../../common/cluster-store";
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
import { dynamicPages } from "./register-page";
import { dynamicPages } from "../../../extensions/register-page";
@observer
export class ClusterManager extends React.Component {

View File

@ -21,7 +21,7 @@ import { ConfirmDialog } from "../confirm-dialog";
import { clusterIpc } from "../../../common/cluster-ipc";
import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route";
import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd";
import { dynamicPages } from "./register-page";
import { dynamicPages } from "../../../extensions/register-page";
interface Props {
className?: IClassName;
@ -150,9 +150,7 @@ export class ClustersMenu extends React.Component<Props> {
</div>
<div className="dynamic-pages">
{dynamicPages.globalPages.map(({ path, components: { MenuIcon } }) => {
if (MenuIcon) {
return <MenuIcon key={path} onClick={() => navigate(path)}/>
}
return <MenuIcon key={path} onClick={() => navigate(path)}/>
})}
</div>
</div>

View File

@ -28,6 +28,7 @@ import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resourc
import { CustomResources } from "../+custom-resources/custom-resources";
import { navigation } from "../../navigation";
import { isAllowedResource } from "../../../common/rbac"
import { dynamicPages } from "../../../extensions/register-page";
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
type SidebarContextValue = {
@ -183,6 +184,18 @@ export class Sidebar extends React.Component<Props> {
>
{this.renderCustomResources()}
</SidebarNavItem>
{dynamicPages.clusterPages.map(({ path, menuTitle, components: { MenuIcon } }) => {
return (
<SidebarNavItem
key={path}
id={`extension-${path}`}
url={path}
routePath={path}
text={menuTitle}
icon={<MenuIcon/>}
/>
)
})}
</div>
</div>
</SidebarContext.Provider>

View File

@ -11,15 +11,9 @@ import { ErrorBoundary } from "./components/error-boundary";
import { WhatsNew, whatsNewRoute } from "./components/+whats-new";
import { Notifications } from "./components/notifications";
import { ConfirmDialog } from "./components/confirm-dialog";
import { extensionStore } from "../extensions/extension-store";
import { getLensRuntime } from "../extensions/lens-runtime";
@observer
export class LensApp extends React.Component {
componentDidMount() {
extensionStore.autoEnableOnLoad(getLensRuntime);
}
render() {
return (
<I18nProvider i18n={_i18n}>