mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
introduce openCommandDialog & closeCommandDialog
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
f54f1c31a4
commit
f38b307d9e
@ -12,6 +12,7 @@ export type CommandContext = {
|
|||||||
export interface CommandRegistration {
|
export interface CommandRegistration {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
scope: "cluster" | "global";
|
||||||
action: (context: CommandContext) => void;
|
action: (context: CommandContext) => void;
|
||||||
isActive?: (context: CommandContext) => boolean;
|
isActive?: (context: CommandContext) => boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,5 +12,6 @@ export const preferencesURL = buildURL(preferencesRoute.path);
|
|||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "app.showPreferences",
|
id: "app.showPreferences",
|
||||||
title: "Preferences: Open",
|
title: "Preferences: Open",
|
||||||
action: () => navigate(preferencesURL.toString())
|
scope: "global",
|
||||||
|
action: () => navigate(preferencesURL())
|
||||||
});
|
});
|
||||||
|
|||||||
56
src/renderer/components/+workspaces/add-workspace.tsx
Normal file
56
src/renderer/components/+workspaces/add-workspace.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { Workspace, workspaceStore } from "../../../common/workspace-store";
|
||||||
|
import { v4 as uuid } from "uuid";
|
||||||
|
import { commandRegistry } from "../../../extensions/registries/command-registry";
|
||||||
|
import { Input, InputValidator } from "../input";
|
||||||
|
import { navigate } from "../../navigation";
|
||||||
|
import { closeCommandDialog, openCommandDialog } from "../command-palette/command-container";
|
||||||
|
|
||||||
|
const uniqueWorkspaceName: InputValidator = {
|
||||||
|
condition: ({ required }) => required,
|
||||||
|
message: () => `Workspace with this name already exists`,
|
||||||
|
validate: value => !workspaceStore.enabledWorkspacesList.find((workspace) => workspace.name === value),
|
||||||
|
};
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class AddWorkspace extends React.Component {
|
||||||
|
handleKeyDown(name: string) {
|
||||||
|
if (name.trim() === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const workspace = workspaceStore.addWorkspace(new Workspace({
|
||||||
|
id: uuid(),
|
||||||
|
name
|
||||||
|
}));
|
||||||
|
|
||||||
|
workspaceStore.setActive(workspace.id);
|
||||||
|
navigate("/");
|
||||||
|
closeCommandDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
placeholder="Workspace name"
|
||||||
|
autoFocus={true}
|
||||||
|
theme="round-black"
|
||||||
|
validators={[uniqueWorkspaceName]}
|
||||||
|
onSubmit={(v) => this.handleKeyDown(v)}
|
||||||
|
dirty={true}
|
||||||
|
showValidationLine={true} />
|
||||||
|
<small className="hint">
|
||||||
|
Please provide a new workspace name (Press "Enter" to confirm or "Escape" to cancel)
|
||||||
|
</small>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commandRegistry.add({
|
||||||
|
id: "workspace.addWorkspace",
|
||||||
|
title: "Workspace: Add workspace ...",
|
||||||
|
scope: "global",
|
||||||
|
action: () => openCommandDialog(<AddWorkspace />)
|
||||||
|
});
|
||||||
@ -1,2 +1 @@
|
|||||||
export * from "./workspaces.route";
|
|
||||||
export * from "./workspaces";
|
export * from "./workspaces";
|
||||||
|
|||||||
71
src/renderer/components/+workspaces/remove-workspace.tsx
Normal file
71
src/renderer/components/+workspaces/remove-workspace.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { computed} from "mobx";
|
||||||
|
import { WorkspaceStore, workspaceStore } from "../../../common/workspace-store";
|
||||||
|
import { ConfirmDialog } from "../confirm-dialog";
|
||||||
|
import { commandRegistry } from "../../../extensions/registries/command-registry";
|
||||||
|
import { Select } from "../select";
|
||||||
|
import { closeCommandDialog, openCommandDialog } from "../command-palette/command-container";
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class RemoveWorkspace extends React.Component {
|
||||||
|
@computed get options() {
|
||||||
|
return workspaceStore.enabledWorkspacesList.filter((workspace) => workspace.id !== WorkspaceStore.defaultId).map((workspace) => {
|
||||||
|
return { value: workspace.id, label: workspace.name };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(id: string) {
|
||||||
|
const workspace = workspaceStore.enabledWorkspacesList.find((workspace) => workspace.id === id);
|
||||||
|
|
||||||
|
if (!workspace ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeCommandDialog();
|
||||||
|
ConfirmDialog.open({
|
||||||
|
okButtonProps: {
|
||||||
|
label: `Remove Workspace`,
|
||||||
|
primary: false,
|
||||||
|
accent: true,
|
||||||
|
},
|
||||||
|
ok: () => {
|
||||||
|
workspaceStore.removeWorkspace(workspace);
|
||||||
|
|
||||||
|
if (workspace.id === workspaceStore.currentWorkspaceId) {
|
||||||
|
workspaceStore.setActive(workspaceStore.enabledWorkspacesList[0].id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
message: (
|
||||||
|
<div className="confirm flex column gaps">
|
||||||
|
<p>
|
||||||
|
Are you sure you want remove workspace <b>{workspace.name}</b>?
|
||||||
|
</p>
|
||||||
|
<p className="info">
|
||||||
|
All clusters within workspace will be cleared as well
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
onChange={(v) => this.onChange(v.value)}
|
||||||
|
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
||||||
|
menuIsOpen={true}
|
||||||
|
options={this.options}
|
||||||
|
autoFocus={true}
|
||||||
|
escapeClearsValue={false}
|
||||||
|
placeholder="Remove workspace" />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commandRegistry.add({
|
||||||
|
id: "workspace.removeWorkspace",
|
||||||
|
title: "Workspace: Remove ...",
|
||||||
|
scope: "global",
|
||||||
|
action: () => openCommandDialog(<RemoveWorkspace />)
|
||||||
|
});
|
||||||
@ -1,7 +0,0 @@
|
|||||||
.WorkspaceMenu {
|
|
||||||
border-radius: $radius;
|
|
||||||
|
|
||||||
.workspaces-title {
|
|
||||||
padding: $padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
import "./workspace-menu.scss";
|
|
||||||
import React from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { workspacesURL } from "./workspaces.route";
|
|
||||||
import { Menu, MenuItem, MenuProps } from "../menu";
|
|
||||||
import { Icon } from "../icon";
|
|
||||||
import { observable } from "mobx";
|
|
||||||
import { WorkspaceId, workspaceStore } from "../../../common/workspace-store";
|
|
||||||
import { cssNames } from "../../utils";
|
|
||||||
import { navigate } from "../../navigation";
|
|
||||||
import { clusterViewURL } from "../cluster-manager/cluster-view.route";
|
|
||||||
import { landingURL } from "../+landing-page";
|
|
||||||
|
|
||||||
interface Props extends Partial<MenuProps> {
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class WorkspaceMenu extends React.Component<Props> {
|
|
||||||
@observable menuVisible = false;
|
|
||||||
|
|
||||||
activateWorkspace = (id: WorkspaceId) => {
|
|
||||||
const clusterId = workspaceStore.getById(id).lastActiveClusterId;
|
|
||||||
|
|
||||||
workspaceStore.setActive(id);
|
|
||||||
|
|
||||||
if (clusterId) {
|
|
||||||
navigate(clusterViewURL({ params: { clusterId } }));
|
|
||||||
} else {
|
|
||||||
navigate(landingURL());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { className, ...menuProps } = this.props;
|
|
||||||
const { enabledWorkspacesList, currentWorkspace } = workspaceStore;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu
|
|
||||||
{...menuProps}
|
|
||||||
usePortal
|
|
||||||
className={cssNames("WorkspaceMenu", className)}
|
|
||||||
isOpen={this.menuVisible}
|
|
||||||
open={() => this.menuVisible = true}
|
|
||||||
close={() => this.menuVisible = false}
|
|
||||||
>
|
|
||||||
<Link className="workspaces-title" to={workspacesURL()}>
|
|
||||||
Workspaces
|
|
||||||
</Link>
|
|
||||||
{enabledWorkspacesList.map(({ id: workspaceId, name, description }) => {
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
key={workspaceId}
|
|
||||||
title={description}
|
|
||||||
active={workspaceId === currentWorkspace.id}
|
|
||||||
onClick={() => this.activateWorkspace(workspaceId)}
|
|
||||||
>
|
|
||||||
<Icon small material="layers"/>
|
|
||||||
<span className="workspace">{name}</span>
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import type { RouteProps } from "react-router";
|
|
||||||
import { buildURL } from "../../../common/utils/buildUrl";
|
|
||||||
|
|
||||||
export const workspacesRoute: RouteProps = {
|
|
||||||
path: "/workspaces"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const workspacesURL = buildURL(workspacesRoute.path);
|
|
||||||
@ -1,224 +1,71 @@
|
|||||||
import "./workspaces.scss";
|
import "./workspaces.scss";
|
||||||
import React, { Fragment } from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { computed, observable, toJS } from "mobx";
|
import { computed} from "mobx";
|
||||||
import { WizardLayout } from "../layout/wizard-layout";
|
import { workspaceStore } from "../../../common/workspace-store";
|
||||||
import { Workspace, WorkspaceId, workspaceStore } from "../../../common/workspace-store";
|
|
||||||
import { v4 as uuid } from "uuid";
|
|
||||||
import { ConfirmDialog } from "../confirm-dialog";
|
|
||||||
import { Icon } from "../icon";
|
|
||||||
import { Input } from "../input";
|
|
||||||
import { cssNames, prevDefault } from "../../utils";
|
|
||||||
import { Button } from "../button";
|
|
||||||
import { isRequired, InputValidator } from "../input/input_validators";
|
|
||||||
import { clusterStore } from "../../../common/cluster-store";
|
|
||||||
import { commandRegistry } from "../../../extensions/registries/command-registry";
|
import { commandRegistry } from "../../../extensions/registries/command-registry";
|
||||||
|
import { Select } from "../select";
|
||||||
import { navigate } from "../../navigation";
|
import { navigate } from "../../navigation";
|
||||||
|
import { closeCommandDialog, openCommandDialog } from "../command-palette/command-container";
|
||||||
|
import { AddWorkspace } from "./add-workspace";
|
||||||
|
import { RemoveWorkspace } from "./remove-workspace";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Workspaces extends React.Component {
|
export class ChooseWorkspace extends React.Component {
|
||||||
@observable editingWorkspaces = observable.map<WorkspaceId, Workspace>();
|
private static addActionId = "__add__";
|
||||||
|
private static removeActionId = "__remove__";
|
||||||
|
|
||||||
@computed get workspaces(): Workspace[] {
|
@computed get options() {
|
||||||
const currentWorkspaces: Map<WorkspaceId, Workspace> = new Map();
|
const options = workspaceStore.enabledWorkspacesList.map((workspace) => {
|
||||||
|
return { value: workspace.id, label: workspace.name };
|
||||||
workspaceStore.enabledWorkspacesList.forEach((w) => {
|
|
||||||
currentWorkspaces.set(w.id, w);
|
|
||||||
});
|
});
|
||||||
const allWorkspaces = new Map([
|
|
||||||
...currentWorkspaces,
|
|
||||||
...this.editingWorkspaces,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return Array.from(allWorkspaces.values());
|
options.push({ value: ChooseWorkspace.addActionId, label: "Add workspace ..." });
|
||||||
|
|
||||||
|
if (options.length > 1) {
|
||||||
|
options.push({ value: ChooseWorkspace.removeActionId, label: "Remove workspace ..." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInfo() {
|
onChange(id: string) {
|
||||||
return (
|
if (id === ChooseWorkspace.addActionId) {
|
||||||
<Fragment>
|
openCommandDialog(<AddWorkspace />);
|
||||||
<h2>What is a Workspace?</h2>
|
|
||||||
<p className="info">
|
|
||||||
Workspaces are used to organize number of clusters into logical groups.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
A single workspaces contains a list of clusters and their full configuration.
|
|
||||||
</p>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
saveWorkspace = (id: WorkspaceId) => {
|
|
||||||
const workspace = new Workspace(this.editingWorkspaces.get(id));
|
|
||||||
|
|
||||||
if (workspaceStore.getById(id)) {
|
|
||||||
workspaceStore.updateWorkspace(workspace);
|
|
||||||
this.clearEditing(id);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workspaceStore.addWorkspace(workspace)) {
|
if (id === ChooseWorkspace.removeActionId) {
|
||||||
this.clearEditing(id);
|
openCommandDialog(<RemoveWorkspace />);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
addWorkspace = () => {
|
|
||||||
const workspaceId = uuid();
|
|
||||||
|
|
||||||
this.editingWorkspaces.set(workspaceId, new Workspace({
|
|
||||||
id: workspaceId,
|
|
||||||
name: "",
|
|
||||||
description: ""
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
editWorkspace = (id: WorkspaceId) => {
|
|
||||||
const workspace = workspaceStore.getById(id);
|
|
||||||
|
|
||||||
this.editingWorkspaces.set(id, toJS(workspace));
|
|
||||||
};
|
|
||||||
|
|
||||||
activateWorkspace = (id: WorkspaceId) => {
|
|
||||||
const clusterId = workspaceStore.getById(id).lastActiveClusterId;
|
|
||||||
|
|
||||||
workspaceStore.setActive(id);
|
workspaceStore.setActive(id);
|
||||||
clusterStore.setActive(clusterId);
|
navigate("/");
|
||||||
};
|
closeCommandDialog();
|
||||||
|
}
|
||||||
clearEditing = (id: WorkspaceId) => {
|
|
||||||
this.editingWorkspaces.delete(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
removeWorkspace = (id: WorkspaceId) => {
|
|
||||||
const workspace = workspaceStore.getById(id);
|
|
||||||
|
|
||||||
ConfirmDialog.open({
|
|
||||||
okButtonProps: {
|
|
||||||
label: `Remove Workspace`,
|
|
||||||
primary: false,
|
|
||||||
accent: true,
|
|
||||||
},
|
|
||||||
ok: () => {
|
|
||||||
this.clearEditing(id);
|
|
||||||
workspaceStore.removeWorkspace(workspace);
|
|
||||||
},
|
|
||||||
message: (
|
|
||||||
<div className="confirm flex column gaps">
|
|
||||||
<p>
|
|
||||||
Are you sure you want remove workspace <b>{workspace.name}</b>?
|
|
||||||
</p>
|
|
||||||
<p className="info">
|
|
||||||
All clusters within workspace will be cleared as well
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onInputKeypress = (evt: React.KeyboardEvent<any>, workspaceId: WorkspaceId) => {
|
|
||||||
if (evt.key == "Enter") {
|
|
||||||
// Trigget input validation
|
|
||||||
evt.currentTarget.blur();
|
|
||||||
evt.currentTarget.focus();
|
|
||||||
this.saveWorkspace(workspaceId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<WizardLayout className="Workspaces" infoPanel={this.renderInfo()}>
|
<Select
|
||||||
<h2>
|
onChange={(v) => this.onChange(v.value)}
|
||||||
Workspaces
|
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
||||||
</h2>
|
menuIsOpen={true}
|
||||||
<div className="items flex column gaps">
|
options={this.options}
|
||||||
{this.workspaces.map(({ id: workspaceId, name, description, ownerRef }) => {
|
autoFocus={true}
|
||||||
const isActive = workspaceStore.currentWorkspaceId === workspaceId;
|
escapeClearsValue={false}
|
||||||
const isDefault = workspaceStore.isDefault(workspaceId);
|
placeholder="Switch to workspace" />
|
||||||
const isEditing = this.editingWorkspaces.has(workspaceId);
|
|
||||||
const editingWorkspace = this.editingWorkspaces.get(workspaceId);
|
|
||||||
const managed = !!ownerRef;
|
|
||||||
const className = cssNames("workspace flex gaps align-center", {
|
|
||||||
active: isActive,
|
|
||||||
editing: isEditing,
|
|
||||||
default: isDefault,
|
|
||||||
});
|
|
||||||
const existenceValidator: InputValidator = {
|
|
||||||
message: () => `Workspace '${name}' already exists`,
|
|
||||||
validate: value => !workspaceStore.getByName(value.trim())
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={workspaceId} className={cssNames(className)}>
|
|
||||||
{!isEditing && (
|
|
||||||
<Fragment>
|
|
||||||
<span className="name flex gaps align-center">
|
|
||||||
<a href="#" onClick={prevDefault(() => this.activateWorkspace(workspaceId))}>{name}</a>
|
|
||||||
{isActive && <span> (current)</span>}
|
|
||||||
</span>
|
|
||||||
<span className="description">{description}</span>
|
|
||||||
{!isDefault && !managed && (
|
|
||||||
<Fragment>
|
|
||||||
<Icon
|
|
||||||
material="edit"
|
|
||||||
tooltip="Edit"
|
|
||||||
onClick={() => this.editWorkspace(workspaceId)}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
material="delete"
|
|
||||||
tooltip="Delete"
|
|
||||||
onClick={() => this.removeWorkspace(workspaceId)}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
{isEditing && (
|
|
||||||
<Fragment>
|
|
||||||
<Input
|
|
||||||
className="name"
|
|
||||||
placeholder={`Name`}
|
|
||||||
value={editingWorkspace.name}
|
|
||||||
onChange={v => editingWorkspace.name = v}
|
|
||||||
onKeyPress={(e) => this.onInputKeypress(e, workspaceId)}
|
|
||||||
validators={[isRequired, existenceValidator]}
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
className="description"
|
|
||||||
placeholder={`Description`}
|
|
||||||
value={editingWorkspace.description}
|
|
||||||
onChange={v => editingWorkspace.description = v}
|
|
||||||
onKeyPress={(e) => this.onInputKeypress(e, workspaceId)}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
material="save"
|
|
||||||
tooltip="Save"
|
|
||||||
onClick={() => this.saveWorkspace(workspaceId)}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
material="cancel"
|
|
||||||
tooltip="Cancel"
|
|
||||||
onClick={() => this.clearEditing(workspaceId)}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
primary
|
|
||||||
className="box left"
|
|
||||||
label="Add Workspace"
|
|
||||||
onClick={this.addWorkspace}
|
|
||||||
/>
|
|
||||||
</WizardLayout>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
commandRegistry.add({
|
commandRegistry.add({
|
||||||
id: "workspace.showList",
|
id: "workspace.chooseWorkspace",
|
||||||
title: "Workspace: Open list",
|
title: "Workspace: Choose...",
|
||||||
action: () => navigate("/workspaces")
|
scope: "global",
|
||||||
|
action: () => openCommandDialog(<ChooseWorkspace />)
|
||||||
});
|
});
|
||||||
|
|||||||
@ -47,6 +47,7 @@ import { nodesStore } from "./+nodes/nodes.store";
|
|||||||
import { podsStore } from "./+workloads-pods/pods.store";
|
import { podsStore } from "./+workloads-pods/pods.store";
|
||||||
import { sum } from "lodash";
|
import { sum } from "lodash";
|
||||||
import { ReplicaSetScaleDialog } from "./+workloads-replicasets/replicaset-scale-dialog";
|
import { ReplicaSetScaleDialog } from "./+workloads-replicasets/replicaset-scale-dialog";
|
||||||
|
import { CommandContainer } from "./command-palette/command-container";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class App extends React.Component {
|
export class App extends React.Component {
|
||||||
@ -203,6 +204,7 @@ export class App extends React.Component {
|
|||||||
<StatefulSetScaleDialog/>
|
<StatefulSetScaleDialog/>
|
||||||
<ReplicaSetScaleDialog/>
|
<ReplicaSetScaleDialog/>
|
||||||
<CronJobTriggerDialog/>
|
<CronJobTriggerDialog/>
|
||||||
|
<CommandContainer listenPaletteOpen={false} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,9 +3,10 @@ import "./bottom-bar.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { WorkspaceMenu } from "../+workspaces/workspace-menu";
|
|
||||||
import { workspaceStore } from "../../../common/workspace-store";
|
import { workspaceStore } from "../../../common/workspace-store";
|
||||||
import { statusBarRegistry } from "../../../extensions/registries";
|
import { statusBarRegistry } from "../../../extensions/registries";
|
||||||
|
import { openCommandDialog } from "../command-palette/command-container";
|
||||||
|
import { ChooseWorkspace } from "../+workspaces";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class BottomBar extends React.Component {
|
export class BottomBar extends React.Component {
|
||||||
@ -16,13 +17,10 @@ export class BottomBar extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="BottomBar flex gaps">
|
<div className="BottomBar flex gaps">
|
||||||
<div id="current-workspace" className="flex gaps align-center">
|
<div id="current-workspace" className="flex gaps align-center" onClick={() => openCommandDialog(<ChooseWorkspace />)}>
|
||||||
<Icon smallest material="layers"/>
|
<Icon smallest material="layers"/>
|
||||||
<span className="workspace-name">{currentWorkspace.name}</span>
|
<span className="workspace-name">{currentWorkspace.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<WorkspaceMenu
|
|
||||||
htmlFor="current-workspace"
|
|
||||||
/>
|
|
||||||
<div className="extensions box grow flex gaps justify-flex-end">
|
<div className="extensions box grow flex gaps justify-flex-end">
|
||||||
{Array.isArray(items) && items.map(({ item }, index) => {
|
{Array.isArray(items) && items.map(({ item }, index) => {
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { ClustersMenu } from "./clusters-menu";
|
|||||||
import { BottomBar } from "./bottom-bar";
|
import { BottomBar } from "./bottom-bar";
|
||||||
import { LandingPage, landingRoute, landingURL } from "../+landing-page";
|
import { LandingPage, landingRoute, landingURL } from "../+landing-page";
|
||||||
import { Preferences, preferencesRoute } from "../+preferences";
|
import { Preferences, preferencesRoute } from "../+preferences";
|
||||||
import { Workspaces, workspacesRoute } from "../+workspaces";
|
|
||||||
import { AddCluster, addClusterRoute } from "../+add-cluster";
|
import { AddCluster, addClusterRoute } from "../+add-cluster";
|
||||||
import { ClusterView } from "./cluster-view";
|
import { ClusterView } from "./cluster-view";
|
||||||
import { ClusterSettings, clusterSettingsRoute } from "../+cluster-settings";
|
import { ClusterSettings, clusterSettingsRoute } from "../+cluster-settings";
|
||||||
@ -67,7 +66,6 @@ export class ClusterManager extends React.Component {
|
|||||||
<Route component={LandingPage} {...landingRoute} />
|
<Route component={LandingPage} {...landingRoute} />
|
||||||
<Route component={Preferences} {...preferencesRoute} />
|
<Route component={Preferences} {...preferencesRoute} />
|
||||||
<Route component={Extensions} {...extensionsRoute} />
|
<Route component={Extensions} {...extensionsRoute} />
|
||||||
<Route component={Workspaces} {...workspacesRoute} />
|
|
||||||
<Route component={AddCluster} {...addClusterRoute} />
|
<Route component={AddCluster} {...addClusterRoute} />
|
||||||
<Route component={ClusterView} {...clusterViewRoute} />
|
<Route component={ClusterView} {...clusterViewRoute} />
|
||||||
<Route component={ClusterSettings} {...clusterSettingsRoute} />
|
<Route component={ClusterSettings} {...clusterSettingsRoute} />
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
#command-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
width: 40%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: var(--dockInfoBackground);
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
import "./command-container.scss";
|
||||||
|
import { action, observable } from "mobx";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import React from "react";
|
||||||
|
import { Dialog } from "../dialog";
|
||||||
|
import { EventEmitter } from "../../../common/event-emitter";
|
||||||
|
import { subscribeToBroadcast } from "../../../common/ipc";
|
||||||
|
import { CommandDialog } from "./command-dialog";
|
||||||
|
|
||||||
|
export type CommandDialogEvent = {
|
||||||
|
component: React.ReactElement
|
||||||
|
};
|
||||||
|
|
||||||
|
const commandDialogBus = new EventEmitter<[CommandDialogEvent]>();
|
||||||
|
|
||||||
|
export function openCommandDialog(component: React.ReactElement) {
|
||||||
|
commandDialogBus.emit({ component });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeCommandDialog() {
|
||||||
|
commandDialogBus.emit({ component: null });
|
||||||
|
}
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class CommandContainer extends React.Component<{listenPaletteOpen: boolean}> {
|
||||||
|
@observable visible = false;
|
||||||
|
@observable commandComponent: React.ReactElement;
|
||||||
|
|
||||||
|
private escHandler(event: KeyboardEvent) {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
private closeDialog() {
|
||||||
|
this.commandComponent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (this.props.listenPaletteOpen) {
|
||||||
|
subscribeToBroadcast("command-palette:open", () => {
|
||||||
|
openCommandDialog(<CommandDialog />);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.addEventListener("keyup", (e) => this.escHandler(e), true);
|
||||||
|
commandDialogBus.addListener((event) => {
|
||||||
|
console.log(event);
|
||||||
|
this.commandComponent = event.component;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Dialog isOpen={!!this.commandComponent} animated={false}>
|
||||||
|
<div id="command-container">
|
||||||
|
{this.commandComponent}
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,28 +0,0 @@
|
|||||||
#command-dialog {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
width: 40%;
|
|
||||||
height: none !important;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
z-index: 1000;
|
|
||||||
background-color: var(--dockInfoBackground);
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin-top: 10px;
|
|
||||||
li {
|
|
||||||
padding-left: 4px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
li:hover {
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +1,17 @@
|
|||||||
|
|
||||||
import "./command-dialog.scss";
|
|
||||||
import { Select } from "../select";
|
import { Select } from "../select";
|
||||||
import { action, computed, observable } from "mobx";
|
import { computed, observable, toJS } from "mobx";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { commandRegistry } from "../../../extensions/registries/command-registry";
|
import { commandRegistry } from "../../../extensions/registries/command-registry";
|
||||||
import { Dialog } from "../dialog";
|
|
||||||
import { clusterStore } from "../../../common/cluster-store";
|
import { clusterStore } from "../../../common/cluster-store";
|
||||||
import { workspaceStore } from "../../../common/workspace-store";
|
import { workspaceStore } from "../../../common/workspace-store";
|
||||||
import { subscribeToBroadcast } from "../../../common/ipc";
|
import { closeCommandDialog } from "./command-container";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class CommandDialog extends React.Component {
|
export class CommandDialog extends React.Component {
|
||||||
@observable visible = false;
|
|
||||||
@observable menuIsOpen = true;
|
@observable menuIsOpen = true;
|
||||||
|
|
||||||
private escHandler(event: KeyboardEvent) {
|
|
||||||
if (event.key === "Escape") {
|
|
||||||
event.stopPropagation();
|
|
||||||
this.closeDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
private shortcutHandler() {
|
|
||||||
this.visible = true;
|
|
||||||
this.menuIsOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private closeDialog() {
|
|
||||||
this.menuIsOpen = false;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.visible = false;
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
window.addEventListener("keyup", (e) => this.escHandler(e), true);
|
|
||||||
subscribeToBroadcast("command-palette:open", () => this.shortcutHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get options() {
|
@computed get options() {
|
||||||
return commandRegistry.getItems().map((command) => {
|
return commandRegistry.getItems().map((command) => {
|
||||||
return { value: command.id, label: command.title };
|
return { value: command.id, label: command.title };
|
||||||
@ -53,32 +25,29 @@ export class CommandDialog extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const action = toJS(command.action);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
command.action({
|
closeCommandDialog();
|
||||||
|
action({
|
||||||
cluster: clusterStore.active,
|
cluster: clusterStore.active,
|
||||||
workspace: workspaceStore.currentWorkspace
|
workspace: workspaceStore.currentWorkspace
|
||||||
});
|
});
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error("failed to execute command", command.id, error);
|
console.error("failed to execute command", command.id, error);
|
||||||
} finally {
|
|
||||||
this.closeDialog();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Dialog isOpen={this.visible}>
|
<Select
|
||||||
<div id="command-dialog">
|
onChange={(v) => this.onChange(v.value)}
|
||||||
<Select
|
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
||||||
onChange={(v) => this.onChange(v.value)}
|
menuIsOpen={this.menuIsOpen}
|
||||||
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
options={this.options}
|
||||||
menuIsOpen={this.menuIsOpen}
|
autoFocus={true}
|
||||||
options={this.options}
|
escapeClearsValue={false}
|
||||||
autoFocus={true}
|
placeholder="" />
|
||||||
escapeClearsValue={false}
|
|
||||||
placeholder="" />
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -242,7 +242,7 @@ export class Input extends React.Component<InputProps, State> {
|
|||||||
|
|
||||||
switch (evt.key) {
|
switch (evt.key) {
|
||||||
case "Enter":
|
case "Enter":
|
||||||
if (this.props.onSubmit && !modified && !evt.repeat) {
|
if (this.props.onSubmit && !modified && !evt.repeat && this.isValid) {
|
||||||
this.props.onSubmit(this.getValue());
|
this.props.onSubmit(this.getValue());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -11,7 +11,8 @@ import { Notifications } from "./components/notifications";
|
|||||||
import { ConfirmDialog } from "./components/confirm-dialog";
|
import { ConfirmDialog } from "./components/confirm-dialog";
|
||||||
import { extensionLoader } from "../extensions/extension-loader";
|
import { extensionLoader } from "../extensions/extension-loader";
|
||||||
import { broadcastMessage } from "../common/ipc";
|
import { broadcastMessage } from "../common/ipc";
|
||||||
import { CommandDialog } from "./components/command-palette/command-dialog";
|
import { } from "./components/command-palette/command-dialog";
|
||||||
|
import { CommandContainer } from "./components/command-palette/command-container";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class LensApp extends React.Component {
|
export class LensApp extends React.Component {
|
||||||
@ -37,7 +38,7 @@ export class LensApp extends React.Component {
|
|||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<Notifications/>
|
<Notifications/>
|
||||||
<ConfirmDialog/>
|
<ConfirmDialog/>
|
||||||
<CommandDialog />
|
<CommandContainer listenPaletteOpen={true} />
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user