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:
parent
f5cea39c64
commit
89bc526e4d
@ -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 React from "react";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
@ -8,10 +8,11 @@ export default class ExampleExtension extends LensExtension {
|
|||||||
onActivate() {
|
onActivate() {
|
||||||
console.log('EXAMPLE EXTENSION: ACTIVATE', this.getMeta())
|
console.log('EXAMPLE EXTENSION: ACTIVATE', this.getMeta())
|
||||||
this.unRegisterPage = this.runtime.dynamicPages.register({
|
this.unRegisterPage = this.runtime.dynamicPages.register({
|
||||||
type: "cluster-view",
|
type: DynamicPageType.CLUSTER,
|
||||||
path: "/extension-example",
|
path: "/extension-example",
|
||||||
|
menuTitle: "Example Extension",
|
||||||
components: {
|
components: {
|
||||||
Main: ExtensionPage,
|
Page: ExtensionPage,
|
||||||
MenuIcon: ExtensionIcon,
|
MenuIcon: ExtensionIcon,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -27,7 +28,7 @@ export function ExtensionIcon(props: {} /*IconProps |*/) {
|
|||||||
return <Icon {...props} material="camera" tooltip={path.basename(__filename)}/>
|
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 {
|
export class ExtensionPage extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -3,6 +3,7 @@ export type { LensRuntimeRendererEnv } from "./lens-runtime";
|
|||||||
|
|
||||||
// APIs
|
// APIs
|
||||||
export * from "./extension"
|
export * from "./extension"
|
||||||
|
export { DynamicPageType } from "./register-page";
|
||||||
|
|
||||||
// Common UI components
|
// Common UI components
|
||||||
export * from "../renderer/components/icon"
|
export * from "../renderer/components/icon"
|
||||||
|
|||||||
@ -1,18 +1,14 @@
|
|||||||
// Lens runtime for injecting to extension on activation
|
// Lens renderer runtime params available to the extension after activation
|
||||||
import { apiManager } from "../renderer/api/api-manager";
|
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
import { dynamicPages } from "../renderer/components/cluster-manager/register-page";
|
import { dynamicPages } from "./register-page";
|
||||||
|
|
||||||
export interface LensRuntimeRendererEnv {
|
export interface LensRuntimeRendererEnv {
|
||||||
apiManager: typeof apiManager;
|
|
||||||
logger: typeof logger;
|
logger: typeof logger;
|
||||||
dynamicPages: typeof dynamicPages
|
dynamicPages: typeof dynamicPages
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: expose more public runtime apis: stores, managers, etc.
|
|
||||||
export function getLensRuntime(): LensRuntimeRendererEnv {
|
export function getLensRuntime(): LensRuntimeRendererEnv {
|
||||||
return {
|
return {
|
||||||
apiManager,
|
|
||||||
logger,
|
logger,
|
||||||
dynamicPages,
|
dynamicPages,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
// Dynamic pages
|
// Extensions-api -> Dynamic pages
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { computed, observable } from "mobx";
|
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 {
|
export interface PageRegistration {
|
||||||
path: string;
|
path: string; // route-path
|
||||||
type: "global" | "cluster-view";
|
menuTitle: string;
|
||||||
|
type: DynamicPageType;
|
||||||
components: PageComponents;
|
components: PageComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,13 +25,14 @@ export class PagesStore {
|
|||||||
protected pages = observable.array<PageRegistration>([], { deep: false });
|
protected pages = observable.array<PageRegistration>([], { deep: false });
|
||||||
|
|
||||||
@computed get globalPages() {
|
@computed get globalPages() {
|
||||||
return this.pages.filter(page => page.type === "global");
|
return this.pages.filter(page => page.type === DynamicPageType.GLOBAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get clusterPages() {
|
@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) {
|
register(params: PageRegistration) {
|
||||||
this.pages.push(params);
|
this.pages.push(params);
|
||||||
return () => {
|
return () => {
|
||||||
@ -10,6 +10,7 @@ import { i18nStore } from "./i18n";
|
|||||||
import { themeStore } from "./theme.store";
|
import { themeStore } from "./theme.store";
|
||||||
import { App } from "./components/app";
|
import { App } from "./components/app";
|
||||||
import { LensApp } from "./lens-app";
|
import { LensApp } from "./lens-app";
|
||||||
|
import { getLensRuntime } from "../extensions/lens-runtime";
|
||||||
|
|
||||||
type AppComponent = React.ComponentType & {
|
type AppComponent = React.ComponentType & {
|
||||||
init?(): void;
|
init?(): void;
|
||||||
@ -32,6 +33,7 @@ export async function bootstrap(App: AppComponent) {
|
|||||||
// init app's dependencies if any
|
// init app's dependencies if any
|
||||||
if (App.init) {
|
if (App.init) {
|
||||||
await App.init();
|
await App.init();
|
||||||
|
extensionStore.autoEnableOnLoad(getLensRuntime);
|
||||||
}
|
}
|
||||||
render(<App/>, rootElem);
|
render(<App/>, rootElem);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store
|
|||||||
import logger from "../../main/logger";
|
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 { dynamicPages } from "../../extensions/register-page";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class App extends React.Component {
|
export class App extends React.Component {
|
||||||
@ -71,6 +72,9 @@ 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}/>
|
||||||
|
{dynamicPages.clusterPages.map(({ path, components: { Page } }) => {
|
||||||
|
return <Route key={path} path={path} component={Page}/>
|
||||||
|
})}
|
||||||
<Redirect exact from="/" to={this.startURL}/>
|
<Redirect exact from="/" to={this.startURL}/>
|
||||||
<Route component={NotFound}/>
|
<Route component={NotFound}/>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { Extensions, extensionsRoute } from "../+extensions";
|
|||||||
import { clusterViewRoute, clusterViewURL, getMatchedCluster, getMatchedClusterId } from "./cluster-view.route";
|
import { clusterViewRoute, clusterViewURL, getMatchedCluster, getMatchedClusterId } from "./cluster-view.route";
|
||||||
import { clusterStore } from "../../../common/cluster-store";
|
import { clusterStore } from "../../../common/cluster-store";
|
||||||
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
|
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
|
||||||
import { dynamicPages } from "./register-page";
|
import { dynamicPages } from "../../../extensions/register-page";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ClusterManager extends React.Component {
|
export class ClusterManager extends React.Component {
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import { ConfirmDialog } from "../confirm-dialog";
|
|||||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||||
import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route";
|
import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route";
|
||||||
import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd";
|
import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd";
|
||||||
import { dynamicPages } from "./register-page";
|
import { dynamicPages } from "../../../extensions/register-page";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: IClassName;
|
className?: IClassName;
|
||||||
@ -150,9 +150,7 @@ export class ClustersMenu extends React.Component<Props> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="dynamic-pages">
|
<div className="dynamic-pages">
|
||||||
{dynamicPages.globalPages.map(({ path, components: { MenuIcon } }) => {
|
{dynamicPages.globalPages.map(({ path, components: { MenuIcon } }) => {
|
||||||
if (MenuIcon) {
|
return <MenuIcon key={path} onClick={() => navigate(path)}/>
|
||||||
return <MenuIcon key={path} onClick={() => navigate(path)}/>
|
|
||||||
}
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -28,6 +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";
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
||||||
type SidebarContextValue = {
|
type SidebarContextValue = {
|
||||||
@ -183,6 +184,18 @@ export class Sidebar extends React.Component<Props> {
|
|||||||
>
|
>
|
||||||
{this.renderCustomResources()}
|
{this.renderCustomResources()}
|
||||||
</SidebarNavItem>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</SidebarContext.Provider>
|
</SidebarContext.Provider>
|
||||||
|
|||||||
@ -11,15 +11,9 @@ import { ErrorBoundary } from "./components/error-boundary";
|
|||||||
import { WhatsNew, whatsNewRoute } from "./components/+whats-new";
|
import { WhatsNew, whatsNewRoute } from "./components/+whats-new";
|
||||||
import { Notifications } from "./components/notifications";
|
import { Notifications } from "./components/notifications";
|
||||||
import { ConfirmDialog } from "./components/confirm-dialog";
|
import { ConfirmDialog } from "./components/confirm-dialog";
|
||||||
import { extensionStore } from "../extensions/extension-store";
|
|
||||||
import { getLensRuntime } from "../extensions/lens-runtime";
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class LensApp extends React.Component {
|
export class LensApp extends React.Component {
|
||||||
componentDidMount() {
|
|
||||||
extensionStore.autoEnableOnLoad(getLensRuntime);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<I18nProvider i18n={_i18n}>
|
<I18nProvider i18n={_i18n}>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user