From 9a074dae761de6fb689c49880d76fd2302d6e561 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 19 Apr 2021 09:24:45 +0300 Subject: [PATCH] hotbar command palette + switching Signed-off-by: Jari Kolehmainen --- src/common/hotbar-store.ts | 60 +++++++++++++++++-- src/migrations/hotbar-store/5.0.0-alpha.0.ts | 8 ++- .../components/hotbar/hotbar-add-command.tsx | 48 +++++++++++++++ .../components/hotbar/hotbar-menu.scss | 6 ++ .../components/hotbar/hotbar-menu.tsx | 43 ++++++++++++- .../hotbar/hotbar-remove-command.tsx | 56 +++++++++++++++++ .../hotbar/hotbar-switch-command.tsx | 57 ++++++++++++++++++ .../components/hotbar/hotbar.commands.tsx | 27 +++++++++ 8 files changed, 296 insertions(+), 9 deletions(-) create mode 100644 src/renderer/components/hotbar/hotbar-add-command.tsx create mode 100644 src/renderer/components/hotbar/hotbar-remove-command.tsx create mode 100644 src/renderer/components/hotbar/hotbar-switch-command.tsx create mode 100644 src/renderer/components/hotbar/hotbar.commands.tsx diff --git a/src/common/hotbar-store.ts b/src/common/hotbar-store.ts index aba7c69bc1..282fe13bf7 100644 --- a/src/common/hotbar-store.ts +++ b/src/common/hotbar-store.ts @@ -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 { @observable hotbars: Hotbar[] = []; + @observable private _activeHotbarId: string; private constructor() { super({ @@ -34,32 +38,78 @@ export class HotbarStore extends BaseStore { }); } + 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 = {}) { 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.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, { diff --git a/src/migrations/hotbar-store/5.0.0-alpha.0.ts b/src/migrations/hotbar-store/5.0.0-alpha.0.ts index 22087ab569..8df45c34dc 100644 --- a/src/migrations/hotbar-store/5.0.0-alpha.0.ts +++ b/src/migrations/hotbar-store/5.0.0-alpha.0.ts @@ -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); } diff --git a/src/renderer/components/hotbar/hotbar-add-command.tsx b/src/renderer/components/hotbar/hotbar-add-command.tsx new file mode 100644 index 0000000000..47b8d7685e --- /dev/null +++ b/src/renderer/components/hotbar/hotbar-add-command.tsx @@ -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 ( + <> + this.onSubmit(v)} + dirty={true} + showValidationLine={true} /> + + Please provide a new hotbar name (Press "Enter" to confirm or "Escape" to cancel) + + + ); + } +} diff --git a/src/renderer/components/hotbar/hotbar-menu.scss b/src/renderer/components/hotbar/hotbar-menu.scss index 22af27e486..3adc4fb91c 100644 --- a/src/renderer/components/hotbar/hotbar-menu.scss +++ b/src/renderer/components/hotbar/hotbar-menu.scss @@ -22,4 +22,10 @@ display: none; } } + + .HotbarSelector { + position: absolute; + bottom: 0; + width: 100%; + } } diff --git a/src/renderer/components/hotbar/hotbar-menu.tsx b/src/renderer/components/hotbar/hotbar-menu.tsx index 05a95c606c..c0c034fc79 100644 --- a/src/renderer/components/hotbar/hotbar-menu.tsx +++ b/src/renderer/components/hotbar/hotbar-menu.tsx @@ -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 { + 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 { 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 { ); })} +
+ this.previous()} /> + this.next()} /> +
); } diff --git a/src/renderer/components/hotbar/hotbar-remove-command.tsx b/src/renderer/components/hotbar/hotbar-remove-command.tsx new file mode 100644 index 0000000000..fbcd3f336b --- /dev/null +++ b/src/renderer/components/hotbar/hotbar-remove-command.tsx @@ -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: ( +
+

+ Are you sure you want remove hotbar {hotbar.name}? +

+
+ ), + }); + } + + render() { + return ( + this.onChange(v.value)} + components={{ DropdownIndicator: null, IndicatorSeparator: null }} + menuIsOpen={true} + options={this.options} + autoFocus={true} + escapeClearsValue={false} + placeholder="Switch to hotbar" /> + ); + } +} diff --git a/src/renderer/components/hotbar/hotbar.commands.tsx b/src/renderer/components/hotbar/hotbar.commands.tsx new file mode 100644 index 0000000000..c2ee3a05dd --- /dev/null +++ b/src/renderer/components/hotbar/hotbar.commands.tsx @@ -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() +}); + +commandRegistry.add({ + id: "hotbar.addHotbar", + title: "Hotbar: Add Hotbar ...", + scope: "global", + action: () => CommandOverlay.open() +}); + +commandRegistry.add({ + id: "hotbar.removeHotbar", + title: "Hotbar: Remove Hotbar ...", + scope: "global", + action: () => CommandOverlay.open() +});