mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
hotbar command palette + switching
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
b8f028825c
commit
9a074dae76
@ -1,6 +1,7 @@
|
||||
import { action, comparer, observable, toJS } from "mobx";
|
||||
import { action, comparer, computed, observable, toJS } from "mobx";
|
||||
import { BaseStore } from "./base-store";
|
||||
import migrations from "../migrations/hotbar-store";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
export interface HotbarItem {
|
||||
entity: {
|
||||
@ -12,16 +13,19 @@ export interface HotbarItem {
|
||||
}
|
||||
|
||||
export interface Hotbar {
|
||||
id: string;
|
||||
name: string;
|
||||
items: HotbarItem[];
|
||||
}
|
||||
|
||||
export interface HotbarStoreModel {
|
||||
hotbars: Hotbar[];
|
||||
activeHotbarId: string;
|
||||
}
|
||||
|
||||
export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
@observable hotbars: Hotbar[] = [];
|
||||
@observable private _activeHotbarId: string;
|
||||
|
||||
private constructor() {
|
||||
super({
|
||||
@ -34,32 +38,78 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
});
|
||||
}
|
||||
|
||||
get activeHotbarId() {
|
||||
return this._activeHotbarId;
|
||||
}
|
||||
|
||||
set activeHotbarId(id: string) {
|
||||
if (this.getByid(id)) {
|
||||
this._activeHotbarId = id;
|
||||
}
|
||||
}
|
||||
|
||||
get activeHotbarIndex() {
|
||||
return this.hotbars.findIndex((hotbar) => hotbar.id === this.activeHotbarId);
|
||||
}
|
||||
|
||||
@action protected async fromStore(data: Partial<HotbarStoreModel> = {}) {
|
||||
if (data.hotbars?.length === 0) {
|
||||
this.hotbars = [{
|
||||
name: "default",
|
||||
id: uuid(),
|
||||
name: "Default",
|
||||
items: []
|
||||
}];
|
||||
} else {
|
||||
this.hotbars = data.hotbars;
|
||||
}
|
||||
|
||||
if (data.activeHotbarId) {
|
||||
if (this.getByid(data.activeHotbarId)) {
|
||||
this.activeHotbarId = data.activeHotbarId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.activeHotbarId) {
|
||||
this.activeHotbarId = this.hotbars[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
getActive() {
|
||||
return this.getByid(this.activeHotbarId);
|
||||
}
|
||||
|
||||
getByName(name: string) {
|
||||
return this.hotbars.find((hotbar) => hotbar.name === name);
|
||||
}
|
||||
|
||||
add(hotbar: Hotbar) {
|
||||
this.hotbars.push(hotbar);
|
||||
getByid(id: string) {
|
||||
return this.hotbars.find((hotbar) => hotbar.id === id);
|
||||
}
|
||||
|
||||
add(hotbar: Partial<Hotbar>) {
|
||||
hotbar.id = uuid();
|
||||
|
||||
if (!hotbar.items) {
|
||||
hotbar.items = [];
|
||||
}
|
||||
|
||||
this.hotbars.push(hotbar as Hotbar);
|
||||
|
||||
return hotbar as Hotbar;
|
||||
}
|
||||
|
||||
remove(hotbar: Hotbar) {
|
||||
this.hotbars = this.hotbars.filter((h) => h !== hotbar);
|
||||
|
||||
if (this.activeHotbarId === hotbar.id) {
|
||||
this.activeHotbarId = this.hotbars[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(): HotbarStoreModel {
|
||||
const model: HotbarStoreModel = {
|
||||
hotbars: this.hotbars
|
||||
hotbars: this.hotbars,
|
||||
activeHotbarId: this.activeHotbarId
|
||||
};
|
||||
|
||||
return toJS(model, {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
import { Hotbar } from "../../common/hotbar-store";
|
||||
import { clusterStore } from "../../common/cluster-store";
|
||||
import { migration } from "../migration-wrapper";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
export default migration({
|
||||
version: "5.0.0-alpha.0",
|
||||
@ -9,11 +10,14 @@ export default migration({
|
||||
const hotbars: Hotbar[] = [];
|
||||
|
||||
clusterStore.enabledClustersList.forEach((cluster: any) => {
|
||||
const name = cluster.workspace || "default";
|
||||
const name = cluster.workspace;
|
||||
|
||||
if (!name) return;
|
||||
|
||||
let hotbar = hotbars.find((h) => h.name === name);
|
||||
|
||||
if (!hotbar) {
|
||||
hotbar = { name, items: [] };
|
||||
hotbar = { id: uuid(), name, items: [] };
|
||||
hotbars.push(hotbar);
|
||||
}
|
||||
|
||||
|
||||
48
src/renderer/components/hotbar/hotbar-add-command.tsx
Normal file
48
src/renderer/components/hotbar/hotbar-add-command.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { hotbarStore } from "../../../common/hotbar-store";
|
||||
import { CommandOverlay } from "../command-palette";
|
||||
import { Input, InputValidator } from "../input";
|
||||
|
||||
const uniqueWorkspaceName: InputValidator = {
|
||||
condition: ({ required }) => required,
|
||||
message: () => `Hotbar with this name already exists`,
|
||||
validate: value => !hotbarStore.getByName(value),
|
||||
};
|
||||
|
||||
@observer
|
||||
export class HotbarAddCommand extends React.Component {
|
||||
|
||||
onSubmit(name: string) {
|
||||
if (!name.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hotbar = hotbarStore.add({
|
||||
name
|
||||
});
|
||||
|
||||
hotbarStore.activeHotbarId = hotbar.id;
|
||||
|
||||
CommandOverlay.close();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
placeholder="Hotbar name"
|
||||
autoFocus={true}
|
||||
theme="round-black"
|
||||
data-test-id="command-palette-hotbar-add-name"
|
||||
validators={[uniqueWorkspaceName]}
|
||||
onSubmit={(v) => this.onSubmit(v)}
|
||||
dirty={true}
|
||||
showValidationLine={true} />
|
||||
<small className="hint">
|
||||
Please provide a new hotbar name (Press "Enter" to confirm or "Escape" to cancel)
|
||||
</small>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -22,4 +22,10 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.HotbarSelector {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
import "./hotbar-menu.scss";
|
||||
import "./hotbar.commands";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { HotbarIcon } from "./hotbar-icon";
|
||||
import { cssNames, IClassName } from "../../utils";
|
||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import { hotbarStore } from "../../../common/hotbar-store";
|
||||
import { catalogEntityRunContext } from "../../api/catalog-entity";
|
||||
import { reaction } from "mobx";
|
||||
import { Notifications } from "../notifications";
|
||||
import { Icon } from "../icon";
|
||||
|
||||
interface Props {
|
||||
className?: IClassName;
|
||||
@ -15,8 +19,19 @@ interface Props {
|
||||
@observer
|
||||
export class HotbarMenu extends React.Component<Props> {
|
||||
|
||||
componentDidMount() {
|
||||
disposeOnUnmount(this, [
|
||||
reaction(() => hotbarStore.activeHotbarId, () => {
|
||||
Notifications.info(`Hotbar "${hotbarStore.getActive().name}" is now active.`, {
|
||||
id: "active-hotbar",
|
||||
timeout: 5_000
|
||||
});
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
get hotbarItems() {
|
||||
const hotbar = hotbarStore.getByName("default"); // FIXME
|
||||
const hotbar = hotbarStore.getActive();
|
||||
|
||||
if (!hotbar) {
|
||||
return [];
|
||||
@ -25,6 +40,26 @@ export class HotbarMenu extends React.Component<Props> {
|
||||
return hotbar.items.map((item) => catalogEntityRegistry.items.find((entity) => entity.metadata.uid === item.entity.uid)).filter(Boolean);
|
||||
}
|
||||
|
||||
previous() {
|
||||
let index = hotbarStore.activeHotbarIndex - 1;
|
||||
|
||||
if (index < 0) {
|
||||
index = hotbarStore.hotbars.length - 1;
|
||||
}
|
||||
|
||||
hotbarStore.activeHotbarId = hotbarStore.hotbars[index].id;
|
||||
}
|
||||
|
||||
next() {
|
||||
let index = hotbarStore.activeHotbarIndex + 1;
|
||||
|
||||
if (index >= hotbarStore.hotbars.length) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
hotbarStore.activeHotbarId = hotbarStore.hotbars[index].id;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className } = this.props;
|
||||
|
||||
@ -43,6 +78,10 @@ export class HotbarMenu extends React.Component<Props> {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="HotbarSelector flex gaps auto">
|
||||
<Icon material="chevron_left" className="previous box" onClick={() => this.previous()} />
|
||||
<Icon material="chevron_right" className="next box" onClick={() => this.next()} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
56
src/renderer/components/hotbar/hotbar-remove-command.tsx
Normal file
56
src/renderer/components/hotbar/hotbar-remove-command.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Select } from "../select";
|
||||
import { computed } from "mobx";
|
||||
import { hotbarStore } from "../../../common/hotbar-store";
|
||||
import { CommandOverlay } from "../command-palette";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
|
||||
@observer
|
||||
export class HotbarRemoveCommand extends React.Component {
|
||||
@computed get options() {
|
||||
return hotbarStore.hotbars.map((hotbar) => {
|
||||
return { value: hotbar.id, label: hotbar.name };
|
||||
});
|
||||
}
|
||||
|
||||
onChange(id: string): void {
|
||||
const hotbar = hotbarStore.getByid(id);
|
||||
|
||||
if (!hotbar) {
|
||||
return;
|
||||
}
|
||||
|
||||
CommandOverlay.close();
|
||||
ConfirmDialog.open({
|
||||
okButtonProps: {
|
||||
label: `Remove Hotbar`,
|
||||
primary: false,
|
||||
accent: true,
|
||||
},
|
||||
ok: () => {
|
||||
hotbarStore.remove(hotbar);
|
||||
},
|
||||
message: (
|
||||
<div className="confirm flex column gaps">
|
||||
<p>
|
||||
Are you sure you want remove hotbar <b>{hotbar.name}</b>?
|
||||
</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 to hotbar" />
|
||||
);
|
||||
}
|
||||
}
|
||||
57
src/renderer/components/hotbar/hotbar-switch-command.tsx
Normal file
57
src/renderer/components/hotbar/hotbar-switch-command.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Select } from "../select";
|
||||
import { computed } from "mobx";
|
||||
import { hotbarStore } from "../../../common/hotbar-store";
|
||||
import { CommandOverlay } from "../command-palette";
|
||||
import { HotbarAddCommand } from "./hotbar-add-command";
|
||||
import { HotbarRemoveCommand } from "./hotbar-remove-command";
|
||||
|
||||
@observer
|
||||
export class HotbarSwitchCommand extends React.Component {
|
||||
private static addActionId = "__add__";
|
||||
private static removeActionId = "__remove__";
|
||||
|
||||
@computed get options() {
|
||||
const options = hotbarStore.hotbars.map((hotbar) => {
|
||||
return { value: hotbar.id, label: hotbar.name };
|
||||
});
|
||||
|
||||
options.push({ value: HotbarSwitchCommand.addActionId, label: "Add hotbar ..." });
|
||||
|
||||
if (hotbarStore.hotbars.length > 1) {
|
||||
options.push({ value: HotbarSwitchCommand.removeActionId, label: "Remove hotbar ..." });
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
onChange(idOrAction: string): void {
|
||||
switch(idOrAction) {
|
||||
case HotbarSwitchCommand.addActionId:
|
||||
CommandOverlay.open(<HotbarAddCommand />);
|
||||
|
||||
return;
|
||||
case HotbarSwitchCommand.removeActionId:
|
||||
CommandOverlay.open(<HotbarRemoveCommand />);
|
||||
|
||||
return;
|
||||
default:
|
||||
hotbarStore.activeHotbarId = idOrAction;
|
||||
CommandOverlay.close();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Select
|
||||
onChange={(v) => this.onChange(v.value)}
|
||||
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
||||
menuIsOpen={true}
|
||||
options={this.options}
|
||||
autoFocus={true}
|
||||
escapeClearsValue={false}
|
||||
placeholder="Switch to hotbar" />
|
||||
);
|
||||
}
|
||||
}
|
||||
27
src/renderer/components/hotbar/hotbar.commands.tsx
Normal file
27
src/renderer/components/hotbar/hotbar.commands.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import { commandRegistry } from "../../../extensions/registries";
|
||||
import { CommandOverlay } from "../command-palette";
|
||||
import { HotbarAddCommand } from "./hotbar-add-command";
|
||||
import { HotbarRemoveCommand } from "./hotbar-remove-command";
|
||||
import { HotbarSwitchCommand } from "./hotbar-switch-command";
|
||||
|
||||
commandRegistry.add({
|
||||
id: "hotbar.switchHotbar",
|
||||
title: "Hotbar: Switch ...",
|
||||
scope: "global",
|
||||
action: () => CommandOverlay.open(<HotbarSwitchCommand />)
|
||||
});
|
||||
|
||||
commandRegistry.add({
|
||||
id: "hotbar.addHotbar",
|
||||
title: "Hotbar: Add Hotbar ...",
|
||||
scope: "global",
|
||||
action: () => CommandOverlay.open(<HotbarAddCommand />)
|
||||
});
|
||||
|
||||
commandRegistry.add({
|
||||
id: "hotbar.removeHotbar",
|
||||
title: "Hotbar: Remove Hotbar ...",
|
||||
scope: "global",
|
||||
action: () => CommandOverlay.open(<HotbarRemoveCommand />)
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user