mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
hotbar context menu & command palette
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
92c1d73700
commit
1f8b388bd4
@ -38,6 +38,7 @@ export type CatalogEntityStatus = {
|
|||||||
|
|
||||||
export interface CatalogEntityActionContext {
|
export interface CatalogEntityActionContext {
|
||||||
navigate: (url: string) => void;
|
navigate: (url: string) => void;
|
||||||
|
setCommandPaletteContext: (context?: CatalogEntity) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CatalogEntityContextMenu = {
|
export type CatalogEntityContextMenu = {
|
||||||
@ -46,7 +47,8 @@ export type CatalogEntityContextMenu = {
|
|||||||
onClick: () => Promise<void>;
|
onClick: () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface CatalogEntityContextMenuContext extends CatalogEntityActionContext {
|
export interface CatalogEntityContextMenuContext {
|
||||||
|
navigate: (url: string) => void;
|
||||||
menuItems: CatalogEntityContextMenu[];
|
menuItems: CatalogEntityContextMenu[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -213,7 +213,6 @@ export class ExtensionLoader {
|
|||||||
registries.globalPageRegistry.add(extension.globalPages, extension),
|
registries.globalPageRegistry.add(extension.globalPages, extension),
|
||||||
registries.globalPageMenuRegistry.add(extension.globalPageMenus, extension),
|
registries.globalPageMenuRegistry.add(extension.globalPageMenus, extension),
|
||||||
registries.appPreferenceRegistry.add(extension.appPreferences),
|
registries.appPreferenceRegistry.add(extension.appPreferences),
|
||||||
registries.clusterFeatureRegistry.add(extension.clusterFeatures),
|
|
||||||
registries.statusBarRegistry.add(extension.statusBarItems),
|
registries.statusBarRegistry.add(extension.statusBarItems),
|
||||||
registries.commandRegistry.add(extension.commands),
|
registries.commandRegistry.add(extension.commands),
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
export type { AppPreferenceRegistration, AppPreferenceComponents } from "../registries/app-preference-registry";
|
export type { AppPreferenceRegistration, AppPreferenceComponents } from "../registries/app-preference-registry";
|
||||||
export type { ClusterFeatureRegistration, ClusterFeatureComponents } from "../registries/cluster-feature-registry";
|
|
||||||
export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../registries/kube-object-detail-registry";
|
export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../registries/kube-object-detail-registry";
|
||||||
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../registries/kube-object-menu-registry";
|
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../registries/kube-object-menu-registry";
|
||||||
export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry";
|
export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { AppPreferenceRegistration, ClusterFeatureRegistration, ClusterPageMenuRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries";
|
import type { AppPreferenceRegistration, ClusterPageMenuRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries";
|
||||||
import type { Cluster } from "../main/cluster";
|
import type { Cluster } from "../main/cluster";
|
||||||
import { LensExtension } from "./lens-extension";
|
import { LensExtension } from "./lens-extension";
|
||||||
import { getExtensionPageUrl } from "./registries/page-registry";
|
import { getExtensionPageUrl } from "./registries/page-registry";
|
||||||
@ -11,7 +11,6 @@ export class LensRendererExtension extends LensExtension {
|
|||||||
clusterPageMenus: ClusterPageMenuRegistration[] = [];
|
clusterPageMenus: ClusterPageMenuRegistration[] = [];
|
||||||
kubeObjectStatusTexts: KubeObjectStatusRegistration[] = [];
|
kubeObjectStatusTexts: KubeObjectStatusRegistration[] = [];
|
||||||
appPreferences: AppPreferenceRegistration[] = [];
|
appPreferences: AppPreferenceRegistration[] = [];
|
||||||
clusterFeatures: ClusterFeatureRegistration[] = [];
|
|
||||||
statusBarItems: StatusBarRegistration[] = [];
|
statusBarItems: StatusBarRegistration[] = [];
|
||||||
kubeObjectDetailItems: KubeObjectDetailRegistration[] = [];
|
kubeObjectDetailItems: KubeObjectDetailRegistration[] = [];
|
||||||
kubeObjectMenuItems: KubeObjectMenuRegistration[] = [];
|
kubeObjectMenuItems: KubeObjectMenuRegistration[] = [];
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
import type React from "react";
|
|
||||||
import { BaseRegistry } from "./base-registry";
|
|
||||||
import { ClusterFeature } from "../cluster-feature";
|
|
||||||
|
|
||||||
export interface ClusterFeatureComponents {
|
|
||||||
Description: React.ComponentType<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ClusterFeatureRegistration {
|
|
||||||
title: string;
|
|
||||||
components: ClusterFeatureComponents
|
|
||||||
feature: ClusterFeature
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ClusterFeatureRegistry extends BaseRegistry<ClusterFeatureRegistration> {
|
|
||||||
}
|
|
||||||
|
|
||||||
export const clusterFeatureRegistry = new ClusterFeatureRegistry();
|
|
||||||
@ -1,23 +1,26 @@
|
|||||||
// Extensions API -> Commands
|
// Extensions API -> Commands
|
||||||
|
|
||||||
import type { Cluster } from "../../main/cluster";
|
|
||||||
import { BaseRegistry } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
import { action } from "mobx";
|
import { action, observable, reaction } from "mobx";
|
||||||
import { LensExtension } from "../lens-extension";
|
import { LensExtension } from "../lens-extension";
|
||||||
|
import { CatalogEntity } from "../../common/catalog-entity";
|
||||||
|
import { catalogEntityRegistry } from "../../common/catalog-entity-registry";
|
||||||
|
|
||||||
export type CommandContext = {
|
export type CommandContext = {
|
||||||
cluster?: Cluster;
|
entity?: CatalogEntity;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface CommandRegistration {
|
export interface CommandRegistration {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
scope: "cluster" | "global";
|
scope: "entity" | "global";
|
||||||
action: (context: CommandContext) => void;
|
action: (context: CommandContext) => void;
|
||||||
isActive?: (context: CommandContext) => boolean;
|
isActive?: (context: CommandContext) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandRegistry extends BaseRegistry<CommandRegistration> {
|
export class CommandRegistry extends BaseRegistry<CommandRegistration> {
|
||||||
|
@observable activeEntity: CatalogEntity;
|
||||||
|
|
||||||
@action
|
@action
|
||||||
add(items: CommandRegistration | CommandRegistration[], extension?: LensExtension) {
|
add(items: CommandRegistration | CommandRegistration[], extension?: LensExtension) {
|
||||||
const itemArray = [items].flat();
|
const itemArray = [items].flat();
|
||||||
@ -32,4 +35,16 @@ export class CommandRegistry extends BaseRegistry<CommandRegistration> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function syncCommandRegistryWithCatalog() {
|
||||||
|
reaction(() => catalogEntityRegistry.items, (items) => {
|
||||||
|
if (!commandRegistry.activeEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!items.includes(commandRegistry.activeEntity)) {
|
||||||
|
commandRegistry.activeEntity = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export const commandRegistry = new CommandRegistry();
|
export const commandRegistry = new CommandRegistry();
|
||||||
|
|||||||
@ -7,6 +7,5 @@ export * from "./app-preference-registry";
|
|||||||
export * from "./status-bar-registry";
|
export * from "./status-bar-registry";
|
||||||
export * from "./kube-object-detail-registry";
|
export * from "./kube-object-detail-registry";
|
||||||
export * from "./kube-object-menu-registry";
|
export * from "./kube-object-menu-registry";
|
||||||
export * from "./cluster-feature-registry";
|
|
||||||
export * from "./kube-object-status-registry";
|
export * from "./kube-object-status-registry";
|
||||||
export * from "./command-registry";
|
export * from "./command-registry";
|
||||||
|
|||||||
@ -4,8 +4,6 @@ import { CatalogEntity, CatalogEntityData } from "../../common/catalog-entity";
|
|||||||
import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../common/catalog-category-registry";
|
import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../common/catalog-category-registry";
|
||||||
import "../../common/catalog-entities";
|
import "../../common/catalog-entities";
|
||||||
|
|
||||||
export { CatalogEntity, CatalogEntityData, CatalogEntityContextMenuContext } from "../../common/catalog-entity";
|
|
||||||
|
|
||||||
export class CatalogEntityRegistry {
|
export class CatalogEntityRegistry {
|
||||||
@observable protected _items: CatalogEntity[] = observable.array([], { deep: true });
|
@observable protected _items: CatalogEntity[] = observable.array([], { deep: true });
|
||||||
|
|
||||||
|
|||||||
12
src/renderer/api/catalog-entity.ts
Normal file
12
src/renderer/api/catalog-entity.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { navigate } from "../navigation";
|
||||||
|
import { commandRegistry } from "../../extensions/registries";
|
||||||
|
import { CatalogEntity } from "../../common/catalog-entity";
|
||||||
|
|
||||||
|
export { CatalogEntity, CatalogEntityData, CatalogEntityActionContext, CatalogEntityContextMenuContext } from "../../common/catalog-entity";
|
||||||
|
|
||||||
|
export const catalogEntityRunContext = {
|
||||||
|
navigate: (url: string) => navigate(url),
|
||||||
|
setCommandPaletteContext: (entity?: CatalogEntity) => {
|
||||||
|
commandRegistry.activeEntity = entity;
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -6,13 +6,13 @@ import { releaseURL } from "../+apps-releases";
|
|||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewHelmCharts",
|
id: "cluster.viewHelmCharts",
|
||||||
title: "Cluster: View Helm Charts",
|
title: "Cluster: View Helm Charts",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(helmChartsURL())
|
action: () => navigate(helmChartsURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewHelmReleases",
|
id: "cluster.viewHelmReleases",
|
||||||
title: "Cluster: View Helm Releases",
|
title: "Cluster: View Helm Releases",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(releaseURL())
|
action: () => navigate(releaseURL())
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { action, computed, reaction } from "mobx";
|
import { action, computed, reaction } from "mobx";
|
||||||
import { CatalogEntity, catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||||
|
import { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
|
||||||
import { ItemObject, ItemStore } from "../../item.store";
|
import { ItemObject, ItemStore } from "../../item.store";
|
||||||
import { autobind } from "../../utils";
|
import { autobind } from "../../utils";
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ export class CatalogEntityItem implements ItemObject {
|
|||||||
return this.entity.metadata.source || "unknown";
|
return this.entity.metadata.source || "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
onRun(ctx: any) {
|
onRun(ctx: CatalogEntityActionContext) {
|
||||||
this.entity.onRun(ctx);
|
this.entity.onRun(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { kebabCase } from "lodash";
|
|||||||
import { PageLayout } from "../layout/page-layout";
|
import { PageLayout } from "../layout/page-layout";
|
||||||
import { MenuItem, MenuActions } from "../menu";
|
import { MenuItem, MenuActions } from "../menu";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { CatalogEntityContextMenuContext } from "../../api/catalog-entity-registry";
|
import { CatalogEntityContextMenuContext, catalogEntityRunContext } from "../../api/catalog-entity";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
import { hotbarStore } from "../../../common/hotbar-store";
|
import { hotbarStore } from "../../../common/hotbar-store";
|
||||||
import { addClusterURL } from "../+add-cluster";
|
import { addClusterURL } from "../+add-cluster";
|
||||||
@ -68,6 +68,10 @@ export class Catalog extends React.Component {
|
|||||||
hotbar.items = hotbar.items.filter((i) => i.entity.uid !== item.id);
|
hotbar.items = hotbar.items.filter((i) => i.entity.uid !== item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDetails(item: CatalogEntityItem) {
|
||||||
|
item.onRun(catalogEntityRunContext);
|
||||||
|
}
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
renderItemMenu(item: CatalogEntityItem) {
|
renderItemMenu(item: CatalogEntityItem) {
|
||||||
const onOpen = async () => {
|
const onOpen = async () => {
|
||||||
@ -124,7 +128,7 @@ export class Catalog extends React.Component {
|
|||||||
item.labels.map((label) => <Badge key={label} label={label} title={label} />),
|
item.labels.map((label) => <Badge key={label} label={label} title={label} />),
|
||||||
{ title: item.phase, className: kebabCase(item.phase) }
|
{ title: item.phase, className: kebabCase(item.phase) }
|
||||||
]}
|
]}
|
||||||
onDetails={(item: CatalogEntityItem) => item.onRun({ navigate: (url: string) => navigate(url)})}
|
onDetails={(item: CatalogEntityItem) => this.onDetails(item) }
|
||||||
renderItemMenu={this.renderItemMenu}
|
renderItemMenu={this.renderItemMenu}
|
||||||
addRemoveButtons={{
|
addRemoveButtons={{
|
||||||
addTooltip: "Add Kubernetes Cluster",
|
addTooltip: "Add Kubernetes Cluster",
|
||||||
|
|||||||
@ -12,5 +12,5 @@ commandRegistry.add({
|
|||||||
clusterId: clusterStore.active.id
|
clusterId: clusterStore.active.id
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
isActive: (context) => !!context.cluster
|
isActive: (context) => !!context.entity
|
||||||
});
|
});
|
||||||
|
|||||||
@ -10,41 +10,41 @@ import { pdbURL } from "../+config-pod-disruption-budgets";
|
|||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewConfigMaps",
|
id: "cluster.viewConfigMaps",
|
||||||
title: "Cluster: View ConfigMaps",
|
title: "Cluster: View ConfigMaps",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(configMapsURL())
|
action: () => navigate(configMapsURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewSecrets",
|
id: "cluster.viewSecrets",
|
||||||
title: "Cluster: View Secrets",
|
title: "Cluster: View Secrets",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(secretsURL())
|
action: () => navigate(secretsURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewResourceQuotas",
|
id: "cluster.viewResourceQuotas",
|
||||||
title: "Cluster: View ResourceQuotas",
|
title: "Cluster: View ResourceQuotas",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(resourceQuotaURL())
|
action: () => navigate(resourceQuotaURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewLimitRanges",
|
id: "cluster.viewLimitRanges",
|
||||||
title: "Cluster: View LimitRanges",
|
title: "Cluster: View LimitRanges",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(limitRangeURL())
|
action: () => navigate(limitRangeURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewHorizontalPodAutoscalers",
|
id: "cluster.viewHorizontalPodAutoscalers",
|
||||||
title: "Cluster: View HorizontalPodAutoscalers (HPA)",
|
title: "Cluster: View HorizontalPodAutoscalers (HPA)",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(hpaURL())
|
action: () => navigate(hpaURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewPodDisruptionBudget",
|
id: "cluster.viewPodDisruptionBudget",
|
||||||
title: "Cluster: View PodDisruptionBudgets",
|
title: "Cluster: View PodDisruptionBudgets",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(pdbURL())
|
action: () => navigate(pdbURL())
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,27 +8,27 @@ import { networkPoliciesURL } from "../+network-policies";
|
|||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewServices",
|
id: "cluster.viewServices",
|
||||||
title: "Cluster: View Services",
|
title: "Cluster: View Services",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(servicesURL())
|
action: () => navigate(servicesURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewEndpoints",
|
id: "cluster.viewEndpoints",
|
||||||
title: "Cluster: View Endpoints",
|
title: "Cluster: View Endpoints",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(endpointURL())
|
action: () => navigate(endpointURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewIngresses",
|
id: "cluster.viewIngresses",
|
||||||
title: "Cluster: View Ingresses",
|
title: "Cluster: View Ingresses",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(ingressURL())
|
action: () => navigate(ingressURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewNetworkPolicies",
|
id: "cluster.viewNetworkPolicies",
|
||||||
title: "Cluster: View NetworkPolicies",
|
title: "Cluster: View NetworkPolicies",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(networkPoliciesURL())
|
action: () => navigate(networkPoliciesURL())
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,6 +5,6 @@ import { nodesURL } from "./nodes.route";
|
|||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewNodes",
|
id: "cluster.viewNodes",
|
||||||
title: "Cluster: View Nodes",
|
title: "Cluster: View Nodes",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(nodesURL())
|
action: () => navigate(nodesURL())
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,41 +5,41 @@ import { cronJobsURL, daemonSetsURL, deploymentsURL, jobsURL, podsURL, statefulS
|
|||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewPods",
|
id: "cluster.viewPods",
|
||||||
title: "Cluster: View Pods",
|
title: "Cluster: View Pods",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(podsURL())
|
action: () => navigate(podsURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewDeployments",
|
id: "cluster.viewDeployments",
|
||||||
title: "Cluster: View Deployments",
|
title: "Cluster: View Deployments",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(deploymentsURL())
|
action: () => navigate(deploymentsURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewDaemonSets",
|
id: "cluster.viewDaemonSets",
|
||||||
title: "Cluster: View DaemonSets",
|
title: "Cluster: View DaemonSets",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(daemonSetsURL())
|
action: () => navigate(daemonSetsURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewStatefulSets",
|
id: "cluster.viewStatefulSets",
|
||||||
title: "Cluster: View StatefulSets",
|
title: "Cluster: View StatefulSets",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(statefulSetsURL())
|
action: () => navigate(statefulSetsURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewJobs",
|
id: "cluster.viewJobs",
|
||||||
title: "Cluster: View Jobs",
|
title: "Cluster: View Jobs",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(jobsURL())
|
action: () => navigate(jobsURL())
|
||||||
});
|
});
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.viewCronJobs",
|
id: "cluster.viewCronJobs",
|
||||||
title: "Cluster: View CronJobs",
|
title: "Cluster: View CronJobs",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => navigate(cronJobsURL())
|
action: () => navigate(cronJobsURL())
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { EventEmitter } from "../../../common/event-emitter";
|
|||||||
import { subscribeToBroadcast } from "../../../common/ipc";
|
import { subscribeToBroadcast } from "../../../common/ipc";
|
||||||
import { CommandDialog } from "./command-dialog";
|
import { CommandDialog } from "./command-dialog";
|
||||||
import { CommandRegistration, commandRegistry } from "../../../extensions/registries/command-registry";
|
import { CommandRegistration, commandRegistry } from "../../../extensions/registries/command-registry";
|
||||||
import { clusterStore } from "../../../common/cluster-store";
|
|
||||||
|
|
||||||
export type CommandDialogEvent = {
|
export type CommandDialogEvent = {
|
||||||
component: React.ReactElement
|
component: React.ReactElement
|
||||||
@ -48,7 +47,7 @@ export class CommandContainer extends React.Component<{ clusterId?: string }> {
|
|||||||
|
|
||||||
private runCommand(command: CommandRegistration) {
|
private runCommand(command: CommandRegistration) {
|
||||||
command.action({
|
command.action({
|
||||||
cluster: clusterStore.active
|
entity: commandRegistry.activeEntity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,11 +16,11 @@ export class CommandDialog extends React.Component {
|
|||||||
|
|
||||||
@computed get options() {
|
@computed get options() {
|
||||||
const context = {
|
const context = {
|
||||||
cluster: clusterStore.active
|
entity: commandRegistry.activeEntity
|
||||||
};
|
};
|
||||||
|
|
||||||
return commandRegistry.getItems().filter((command) => {
|
return commandRegistry.getItems().filter((command) => {
|
||||||
if (command.scope === "cluster" && !clusterStore.active) {
|
if (command.scope === "entity" && !clusterStore.active) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,15 +54,15 @@ export class CommandDialog extends React.Component {
|
|||||||
|
|
||||||
if (command.scope === "global") {
|
if (command.scope === "global") {
|
||||||
action({
|
action({
|
||||||
cluster: clusterStore.active
|
entity: commandRegistry.activeEntity
|
||||||
});
|
});
|
||||||
} else if(clusterStore.active) {
|
} else if(commandRegistry.activeEntity) {
|
||||||
navigate(clusterViewURL({
|
navigate(clusterViewURL({
|
||||||
params: {
|
params: {
|
||||||
clusterId: clusterStore.active.id
|
clusterId: commandRegistry.activeEntity.metadata.uid
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
broadcastMessage(`command-palette:run-action:${clusterStore.active.id}`, command.id);
|
broadcastMessage(`command-palette:run-action:${commandRegistry.activeEntity.metadata.uid}`, command.id);
|
||||||
}
|
}
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error("[COMMAND-DIALOG] failed to execute command", command.id, error);
|
console.error("[COMMAND-DIALOG] failed to execute command", command.id, error);
|
||||||
|
|||||||
@ -136,7 +136,7 @@ export class Dock extends React.Component<Props> {
|
|||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "cluster.openTerminal",
|
id: "cluster.openTerminal",
|
||||||
title: "Cluster: Open terminal",
|
title: "Cluster: Open terminal",
|
||||||
scope: "cluster",
|
scope: "entity",
|
||||||
action: () => createTerminalTab(),
|
action: () => createTerminalTab(),
|
||||||
isActive: (context) => !!context.cluster
|
isActive: (context) => !!context.entity
|
||||||
});
|
});
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export class ErrorBoundary extends React.Component<Props, State> {
|
|||||||
if (error) {
|
if (error) {
|
||||||
const slackLink = <a href={slackUrl} rel="noreferrer" target="_blank">Slack</a>;
|
const slackLink = <a href={slackUrl} rel="noreferrer" target="_blank">Slack</a>;
|
||||||
const githubLink = <a href={issuesTrackerUrl} rel="noreferrer" target="_blank">Github</a>;
|
const githubLink = <a href={issuesTrackerUrl} rel="noreferrer" target="_blank">Github</a>;
|
||||||
const pageUrl = location.href;
|
const pageUrl = location.pathname;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ErrorBoundary flex column gaps">
|
<div className="ErrorBoundary flex column gaps">
|
||||||
|
|||||||
@ -5,7 +5,12 @@ import { observer } from "mobx-react";
|
|||||||
import { cssNames, IClassName } from "../../utils";
|
import { cssNames, IClassName } from "../../utils";
|
||||||
import { Tooltip } from "../tooltip";
|
import { Tooltip } from "../tooltip";
|
||||||
import { Avatar } from "@material-ui/core";
|
import { Avatar } from "@material-ui/core";
|
||||||
import { CatalogEntity } from "../../../common/catalog-entity";
|
import { CatalogEntity, CatalogEntityContextMenuContext } from "../../../common/catalog-entity";
|
||||||
|
import { Menu, MenuItem } from "../menu";
|
||||||
|
import { Icon } from "../icon";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
import { navigate } from "../../navigation";
|
||||||
|
import { hotbarStore } from "../../../common/hotbar-store";
|
||||||
|
|
||||||
interface Props extends DOMAttributes<HTMLElement> {
|
interface Props extends DOMAttributes<HTMLElement> {
|
||||||
entity: CatalogEntity;
|
entity: CatalogEntity;
|
||||||
@ -16,6 +21,16 @@ interface Props extends DOMAttributes<HTMLElement> {
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class HotbarIcon extends React.Component<Props> {
|
export class HotbarIcon extends React.Component<Props> {
|
||||||
|
@observable.deep private contextMenu: CatalogEntityContextMenuContext;
|
||||||
|
@observable menuOpen = false;
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.contextMenu = {
|
||||||
|
menuItems: [],
|
||||||
|
navigate: (url: string) => navigate(url)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
get iconString() {
|
get iconString() {
|
||||||
let splittedName = this.props.entity.metadata.name.split(" ");
|
let splittedName = this.props.entity.metadata.name.split(" ");
|
||||||
|
|
||||||
@ -36,6 +51,20 @@ export class HotbarIcon extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleMenu() {
|
||||||
|
this.menuOpen = !this.menuOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFromHotbar(item: CatalogEntity) {
|
||||||
|
const hotbar = hotbarStore.getByName("default"); // FIXME
|
||||||
|
|
||||||
|
if (!hotbar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hotbar.items = hotbar.items.filter((i) => i.entity.uid !== item.metadata.uid);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
entity, errorClass, isActive,
|
entity, errorClass, isActive,
|
||||||
@ -46,11 +75,34 @@ export class HotbarIcon extends React.Component<Props> {
|
|||||||
interactive: true,
|
interactive: true,
|
||||||
active: isActive,
|
active: isActive,
|
||||||
});
|
});
|
||||||
|
const onOpen = async () => {
|
||||||
|
await entity.onContextMenuOpen(this.contextMenu);
|
||||||
|
this.toggleMenu();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...elemProps} className={className} id={entityIconId}>
|
<div {...elemProps} className={className} id={entityIconId}>
|
||||||
<Tooltip targetId={entityIconId}>{entity.metadata.name}</Tooltip>
|
<Tooltip targetId={entityIconId}>{entity.metadata.name}</Tooltip>
|
||||||
<Avatar variant="square" className={isActive ? "active" : "default"}>{this.iconString}</Avatar>
|
<Avatar variant="square" className={isActive ? "active" : "default"}>{this.iconString}</Avatar>
|
||||||
|
<Menu
|
||||||
|
usePortal={true}
|
||||||
|
htmlFor={entityIconId}
|
||||||
|
isOpen={this.menuOpen}
|
||||||
|
toggleEvent="contextmenu"
|
||||||
|
position={{right: true, top: true}} // FIXME: position does not work
|
||||||
|
open={() => onOpen()}
|
||||||
|
close={() => this.toggleMenu()}>
|
||||||
|
<MenuItem key="remove-from-hotbar" onClick={() => this.removeFromHotbar(entity) }>
|
||||||
|
<Icon material="clear" interactive={true} title="Remove from hotbar"/> Remove from Hotbar
|
||||||
|
</MenuItem>
|
||||||
|
{ this.contextMenu && this.contextMenu.menuItems.map((menuItem) => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={menuItem.title} onClick={() => menuItem.onClick()}>
|
||||||
|
<Icon material={menuItem.icon} interactive={true} title={menuItem.title}/> {menuItem.title}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Menu>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import { observer } from "mobx-react";
|
|||||||
import { HotbarIcon } from "./hotbar-icon";
|
import { HotbarIcon } from "./hotbar-icon";
|
||||||
import { cssNames, IClassName } from "../../utils";
|
import { cssNames, IClassName } from "../../utils";
|
||||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||||
import { navigate } from "../../navigation";
|
|
||||||
import { hotbarStore } from "../../../common/hotbar-store";
|
import { hotbarStore } from "../../../common/hotbar-store";
|
||||||
|
import { catalogEntityRunContext } from "../../api/catalog-entity";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: IClassName;
|
className?: IClassName;
|
||||||
@ -23,9 +23,6 @@ export class HotbarMenu extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const items = hotbar.items.map((item) => catalogEntityRegistry.items.find((entity) => entity.metadata.uid === item.entity.uid)).filter(Boolean);
|
const items = hotbar.items.map((item) => catalogEntityRegistry.items.find((entity) => entity.metadata.uid === item.entity.uid)).filter(Boolean);
|
||||||
const runContext = {
|
|
||||||
navigate: (url: string) => navigate(url)
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cssNames("HotbarMenu flex column", className)}>
|
<div className={cssNames("HotbarMenu flex column", className)}>
|
||||||
@ -36,7 +33,7 @@ export class HotbarMenu extends React.Component<Props> {
|
|||||||
key={entity.metadata.uid}
|
key={entity.metadata.uid}
|
||||||
entity={entity}
|
entity={entity}
|
||||||
isActive={entity.status.active}
|
isActive={entity.status.active}
|
||||||
onClick={() => entity.onRun(runContext)}
|
onClick={() => entity.onRun(catalogEntityRunContext)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -17,6 +17,8 @@ import { registerIpcHandlers } from "./ipc";
|
|||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
import { IpcRendererNavigationEvents } from "./navigation/events";
|
import { IpcRendererNavigationEvents } from "./navigation/events";
|
||||||
import { catalogEntityRegistry } from "./api/catalog-entity-registry";
|
import { catalogEntityRegistry } from "./api/catalog-entity-registry";
|
||||||
|
import { commandRegistry } from "../extensions/registries";
|
||||||
|
import { reaction } from "mobx";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class LensApp extends React.Component {
|
export class LensApp extends React.Component {
|
||||||
@ -36,6 +38,18 @@ export class LensApp extends React.Component {
|
|||||||
ipcRenderer.send(IpcRendererNavigationEvents.LOADED);
|
ipcRenderer.send(IpcRendererNavigationEvents.LOADED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
reaction(() => catalogEntityRegistry.items, (items) => {
|
||||||
|
if (!commandRegistry.activeEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!items.includes(commandRegistry.activeEntity)) {
|
||||||
|
commandRegistry.activeEntity = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user