mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
fixes, tweak example-extension for demo
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
b7be386e6b
commit
cd7f906afc
@ -1,12 +1,19 @@
|
|||||||
import { LensRendererExtension, Component } from "@k8slens/extensions";
|
import { Component, LensRendererExtension, Navigation } from "@k8slens/extensions";
|
||||||
import { CoffeeDoodle } from "react-open-doodles";
|
import { CoffeeDoodle } from "react-open-doodles";
|
||||||
import path from "path";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import path from "path";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
|
||||||
|
export const exampleNameUrlParam = Navigation.createUrlParam<string>({
|
||||||
|
name: "name",
|
||||||
|
defaultValue: "demo",
|
||||||
|
});
|
||||||
|
|
||||||
export function ExampleIcon(props: Component.IconProps) {
|
export function ExampleIcon(props: Component.IconProps) {
|
||||||
return <Component.Icon {...props} material="pages" tooltip={path.basename(__filename)}/>;
|
return <Component.Icon {...props} material="pages" tooltip={path.basename(__filename)}/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@observer
|
||||||
export class ExamplePage extends React.Component<{ extension: LensRendererExtension }> {
|
export class ExamplePage extends React.Component<{ extension: LensRendererExtension }> {
|
||||||
deactivate = () => {
|
deactivate = () => {
|
||||||
const { extension } = this.props;
|
const { extension } = this.props;
|
||||||
@ -15,16 +22,24 @@ export class ExamplePage extends React.Component<{ extension: LensRendererExtens
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const exampleName = exampleNameUrlParam.get();
|
||||||
const doodleStyle = {
|
const doodleStyle = {
|
||||||
width: "200px"
|
width: "200px"
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex column gaps align-flex-start">
|
<div className="flex column gaps align-flex-start" style={{ padding: 24 }}>
|
||||||
<div style={doodleStyle}><CoffeeDoodle accent="#3d90ce"/></div>
|
<div style={doodleStyle}><CoffeeDoodle accent="#3d90ce"/></div>
|
||||||
|
|
||||||
<p>Hello from Example extension!</p>
|
<p>Hello from Example extension!</p>
|
||||||
<p>File: <i>{__filename}</i></p>
|
<p>File: <i>{__filename}</i></p>
|
||||||
|
<p>Location: <i>{location.href}</i></p>
|
||||||
|
|
||||||
|
<p className="url-params-demo flex column gaps">
|
||||||
|
<a onClick={() => exampleNameUrlParam.set("secret")}>Show secret button</a>
|
||||||
|
{exampleName === "secret" && (
|
||||||
<Component.Button accent label="Deactivate" onClick={this.deactivate}/>
|
<Component.Button accent label="Deactivate" onClick={this.deactivate}/>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { LensRendererExtension } from "@k8slens/extensions";
|
import { LensRendererExtension } from "@k8slens/extensions";
|
||||||
import { ExampleIcon, ExamplePage } from "./page";
|
import { ExampleIcon, ExamplePage, exampleNameUrlParam } from "./page";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export default class ExampleExtension extends LensRendererExtension {
|
export default class ExampleExtension extends LensRendererExtension {
|
||||||
@ -9,13 +9,16 @@ export default class ExampleExtension extends LensRendererExtension {
|
|||||||
title: "Example Extension",
|
title: "Example Extension",
|
||||||
components: {
|
components: {
|
||||||
Page: () => <ExamplePage extension={this}/>,
|
Page: () => <ExamplePage extension={this}/>,
|
||||||
}
|
},
|
||||||
|
params: [
|
||||||
|
exampleNameUrlParam,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
clusterPageMenus = [
|
clusterPageMenus = [
|
||||||
{
|
{
|
||||||
target: { pageId: "example", params: {} },
|
target: { pageId: "example" },
|
||||||
title: "Example Extension",
|
title: "Example Extension",
|
||||||
components: {
|
components: {
|
||||||
Icon: ExampleIcon,
|
Icon: ExampleIcon,
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export interface PageComponents {
|
|||||||
export interface PageTarget<P = {}> {
|
export interface PageTarget<P = {}> {
|
||||||
extensionId?: string;
|
extensionId?: string;
|
||||||
pageId?: string;
|
pageId?: string;
|
||||||
params?: Record<string, any | any[]> & P;
|
params?: Record<string, any | any[]> & P; // default target page params
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RegisteredPage extends PageRegistration {
|
export interface RegisteredPage extends PageRegistration {
|
||||||
@ -38,26 +38,23 @@ export interface RegisteredPage extends PageRegistration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getExtensionPageUrl<P extends object>(target: PageTarget): string {
|
export function getExtensionPageUrl<P extends object>(target: PageTarget): string {
|
||||||
const { extensionId, pageId = "", params } = target;
|
const { extensionId, pageId = "", params: targetParams = {} } = target;
|
||||||
let stringifiedParams = "";
|
let stringifiedParams = "";
|
||||||
|
|
||||||
// stringify params to matched target page
|
// stringify params to matched target page
|
||||||
if (params) {
|
|
||||||
const page = globalPageRegistry.getByPageTarget(target) || clusterPageRegistry.getByPageTarget(target);
|
const page = globalPageRegistry.getByPageTarget(target) || clusterPageRegistry.getByPageTarget(target);
|
||||||
if (page?.params) {
|
if (page?.params) {
|
||||||
const searchParams: string[] = [];
|
const searchParams = page.params.map(urlParam => {
|
||||||
page.params.forEach(urlParam => {
|
return urlParam.toSearchString({
|
||||||
const paramValue = params[urlParam.name];
|
value: targetParams[urlParam.name] ?? urlParam.getDefaultValue(),
|
||||||
if (paramValue == undefined) return;
|
mergeGlobals: false,
|
||||||
searchParams.push(
|
withPrefix: false,
|
||||||
urlParam.toSearchString(paramValue, { mergeGlobals: false, withPrefix: false }) // e.g. "param=value"
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
if (searchParams.length > 0) {
|
if (searchParams.length > 0) {
|
||||||
stringifiedParams = `?${searchParams.join("&")}`;
|
stringifiedParams = `?${searchParams.join("&")}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return path.posix.join("/extension", sanitizeExtensionName(extensionId), pageId, stringifiedParams);
|
return path.posix.join("/extension", sanitizeExtensionName(extensionId), pageId, stringifiedParams);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export { navigate, UrlParamInit, isActiveRoute, createUrlParam, UrlParam } from "../../renderer/navigation";
|
export { createUrlParam, navigate, isActiveRoute, UrlParamInit, UrlParam } from "../../renderer/navigation";
|
||||||
export { hideDetails, showDetails, getDetailsUrl } from "../../renderer/components/kube-object/kube-object-details";
|
export { hideDetails, showDetails, getDetailsUrl } from "../../renderer/components/kube-object/kube-object-details";
|
||||||
export { IURLParams } from "../../common/utils/buildUrl";
|
export { IURLParams } from "../../common/utils/buildUrl";
|
||||||
|
|||||||
@ -34,7 +34,7 @@ import { Terminal } from "./dock/terminal";
|
|||||||
import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store";
|
import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store";
|
||||||
import logger from "../../main/logger";
|
import logger from "../../main/logger";
|
||||||
import { webFrame } from "electron";
|
import { webFrame } from "electron";
|
||||||
import { clusterPageRegistry } from "../../extensions/registries/page-registry";
|
import { clusterPageRegistry, getExtensionPageUrl } from "../../extensions/registries/page-registry";
|
||||||
import { extensionLoader } from "../../extensions/extension-loader";
|
import { extensionLoader } from "../../extensions/extension-loader";
|
||||||
import { appEventBus } from "../../common/event-bus";
|
import { appEventBus } from "../../common/event-bus";
|
||||||
import { broadcastMessage, requestMain } from "../../common/ipc";
|
import { broadcastMessage, requestMain } from "../../common/ipc";
|
||||||
@ -126,14 +126,14 @@ export class App extends React.Component {
|
|||||||
if (!menuItem.id) {
|
if (!menuItem.id) {
|
||||||
return routes;
|
return routes;
|
||||||
}
|
}
|
||||||
clusterPageMenuRegistry.getSubItems(menuItem).forEach((item) => {
|
clusterPageMenuRegistry.getSubItems(menuItem).forEach((subMenu) => {
|
||||||
const page = clusterPageRegistry.getByPageTarget(item.target);
|
const page = clusterPageRegistry.getByPageTarget(subMenu.target);
|
||||||
|
|
||||||
if (page) {
|
if (page) {
|
||||||
routes.push({
|
routes.push({
|
||||||
routePath: page.url,
|
routePath: page.url,
|
||||||
url: page.url,
|
url: getExtensionPageUrl(subMenu.target),
|
||||||
title: item.title,
|
title: subMenu.title,
|
||||||
component: page.components.Page,
|
component: page.components.Page,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { landingURL } from "../+landing-page";
|
|||||||
import { Tooltip } from "../tooltip";
|
import { Tooltip } from "../tooltip";
|
||||||
import { ConfirmDialog } from "../confirm-dialog";
|
import { ConfirmDialog } from "../confirm-dialog";
|
||||||
import { clusterViewURL } from "./cluster-view.route";
|
import { clusterViewURL } from "./cluster-view.route";
|
||||||
import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
import { getExtensionPageUrl, globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
||||||
import { clusterDisconnectHandler } from "../../../common/cluster-ipc";
|
import { clusterDisconnectHandler } from "../../../common/cluster-ipc";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -159,13 +159,16 @@ export class ClustersMenu extends React.Component<Props> {
|
|||||||
<div className="extensions">
|
<div className="extensions">
|
||||||
{globalPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
{globalPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||||
const registeredPage = globalPageRegistry.getByPageTarget(target);
|
const registeredPage = globalPageRegistry.getByPageTarget(target);
|
||||||
if (!registeredPage) return;
|
if (!registeredPage){
|
||||||
const { url: pageUrl } = registeredPage;
|
return;
|
||||||
|
}
|
||||||
|
const pageUrl = getExtensionPageUrl(target);
|
||||||
|
const isActive = isActiveRoute(registeredPage.url);
|
||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
key={pageUrl}
|
key={pageUrl}
|
||||||
tooltip={title}
|
tooltip={title}
|
||||||
active={isActiveRoute(pageUrl)}
|
active={isActive}
|
||||||
onClick={() => navigate(pageUrl)}
|
onClick={() => navigate(pageUrl)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export function hideDetails() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getDetailsUrl(details: string, resetSelected = false) {
|
export function getDetailsUrl(details: string, resetSelected = false) {
|
||||||
const detailsUrl = kubeDetailsUrlParam.toSearchString(details);
|
const detailsUrl = kubeDetailsUrlParam.toSearchString({ value: details });
|
||||||
if (resetSelected) {
|
if (resetSelected) {
|
||||||
const params = new URLSearchParams(detailsUrl);
|
const params = new URLSearchParams(detailsUrl);
|
||||||
params.delete(kubeSelectedUrlParam.name);
|
params.delete(kubeSelectedUrlParam.name);
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import { CustomResources } from "../+custom-resources/custom-resources";
|
|||||||
import { isActiveRoute } from "../../navigation";
|
import { isActiveRoute } from "../../navigation";
|
||||||
import { isAllowedResource } from "../../../common/rbac";
|
import { isAllowedResource } from "../../../common/rbac";
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import { ClusterPageMenuRegistration, clusterPageMenuRegistry, clusterPageRegistry } from "../../../extensions/registries";
|
import { ClusterPageMenuRegistration, clusterPageMenuRegistry, clusterPageRegistry, getExtensionPageUrl } from "../../../extensions/registries";
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
||||||
|
|
||||||
@ -84,13 +84,14 @@ export class Sidebar extends React.Component<Props> {
|
|||||||
return routes;
|
return routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterPageMenuRegistry.getSubItems(menu).forEach((subItem) => {
|
clusterPageMenuRegistry.getSubItems(menu).forEach((subMenu) => {
|
||||||
const subPage = clusterPageRegistry.getByPageTarget(subItem.target);
|
const subPage = clusterPageRegistry.getByPageTarget(subMenu.target);
|
||||||
if (subPage) {
|
if (subPage) {
|
||||||
|
const { extensionId, id: pageId } = subPage;
|
||||||
routes.push({
|
routes.push({
|
||||||
routePath: subPage.url,
|
routePath: subPage.url,
|
||||||
url: subPage.url,
|
url: getExtensionPageUrl({ extensionId, pageId, params: subMenu.target.params }),
|
||||||
title: subItem.title,
|
title: subMenu.title,
|
||||||
component: subPage.components.Page,
|
component: subPage.components.Page,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -106,12 +107,12 @@ export class Sidebar extends React.Component<Props> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pageUrl = getExtensionPageUrl(menuItem.target);
|
||||||
|
let isActive = isActiveRoute(registeredPage.url);
|
||||||
const tabRoutes = this.getTabLayoutRoutes(menuItem);
|
const tabRoutes = this.getTabLayoutRoutes(menuItem);
|
||||||
let pageUrl = registeredPage.url;
|
|
||||||
let isActive = isActiveRoute(pageUrl);
|
|
||||||
|
|
||||||
if (tabRoutes.length > 0) {
|
if (tabRoutes.length > 0) {
|
||||||
pageUrl = (tabRoutes.find(tab => tab.default) || tabRoutes[0]).url;
|
pageUrl = tabRoutes[0].url;
|
||||||
isActive = isActiveRoute(tabRoutes.map((tab) => tab.routePath));
|
isActive = isActiveRoute(tabRoutes.map((tab) => tab.routePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export class UrlParam<V = any | any[]> {
|
|||||||
|
|
||||||
get(): V {
|
get(): V {
|
||||||
const { history, urlName } = this;
|
const { history, urlName } = this;
|
||||||
const { multiValueSep, multiValues, defaultValue, skipEmpty } = this.init;
|
const { multiValueSep, defaultValue, skipEmpty } = this.init;
|
||||||
const value = this.parse(history.searchParams.getAsArray(urlName, multiValueSep));
|
const value = this.parse(history.searchParams.getAsArray(urlName, multiValueSep));
|
||||||
|
|
||||||
if (skipEmpty && this.isEmpty(value)) {
|
if (skipEmpty && this.isEmpty(value)) {
|
||||||
@ -64,19 +64,23 @@ export class UrlParam<V = any | any[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set(value: V, { mergeGlobals = true, replaceHistory = false } = {}) {
|
set(value: V, { mergeGlobals = true, replaceHistory = false } = {}) {
|
||||||
const search = this.toSearchString(value, { mergeGlobals });
|
const search = this.toSearchString({ mergeGlobals, value });
|
||||||
this.history.merge({ search }, replaceHistory);
|
this.history.merge({ search }, replaceHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefaultValue(){
|
||||||
|
return this.init.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
isDefault() {
|
isDefault() {
|
||||||
return this.get() === this.init.defaultValue;
|
return this.get() === this.getDefaultValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.history.searchParams.delete(this.urlName);
|
this.history.searchParams.delete(this.urlName);
|
||||||
}
|
}
|
||||||
|
|
||||||
toSearchString(value = this.get(), { withPrefix = true, mergeGlobals = true } = {}): string {
|
toSearchString({ withPrefix = true, mergeGlobals = true, value = this.get() } = {}): string {
|
||||||
const { history, urlName, init: { skipEmpty } } = this;
|
const { history, urlName, init: { skipEmpty } } = this;
|
||||||
const searchParams = new URLSearchParams(mergeGlobals ? history.location.search : "");
|
const searchParams = new URLSearchParams(mergeGlobals ? history.location.search : "");
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user