Remove BaseStore<T> and replace with composition of dependencies (#7002)

* Rename ExtensionStore -> BaseExtensionStore
- The name was too close to ExtensionsStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move ExtensionsStore to new format

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move ClusterStore to new format

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move UserStore to new format

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Cleanup types to remove multiple cast locations

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move HotbarStore to new format

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move WeblinkStore to new format

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move FileSystemProvisionerStore to new format

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update snapshots

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Clean up impl and rename to better describe intent

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix remaining type errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fully split apart the enabled extensions storage

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fully split apart the clusters storage

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fully split apart the hotbar storage

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fully split apart the weblinks storage

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fully split apart the user preferences storage

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix crashing

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests and snapshots

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix integration test failures

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Improve typing to prevent errors in the future.

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Cleanup @k8slens/messaging and friends

- To fix type errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix lint issue

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix type errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix global override not being complete enough causing tests to fail

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Bump memory for unit tests on CI

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Attempt to fix memory issue on CI again

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fixup test because of new injectables

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Upgrade Jest

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix unit tests falling over

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Back out jest config change

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove console log

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update snapshot

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests by matching equality instead of snapshots

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests by forcing specific snapshot style

- Ubuntu CI seems to format arrays in snapshots differently than macOS locally

Signed-off-by: Sebastian Malton <sebastian@malton.name>

---------

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton authored and GitHub committed 2023-03-28 17:54:25 +03:00
1 parent ba4a283af9
commit 49f0a1af9c
365 files changed
+6733 -3972

No files matched your search

@@ -0,0 +1,97 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<HotbarRemoveCommand /> renders w/o errors 1`] = `
<div>
<div
class="Select theme-dark css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
>
<span
id="aria-selection"
>
option , selected.
</span>
<span
id="aria-context"
>
2 results available. Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu.
</span>
</span>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control Select__control--is-focused Select__control--menu-is-open css-t3ipsp-control"
>
<div
class="Select__value-container css-1fdsijx-ValueContainer"
>
<div
class="Select__placeholder css-1jqq78o-placeholder"
id="react-select-2-placeholder"
>
Remove hotbar
</div>
<div
class="Select__input-container css-qbdosj-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-controls="react-select-2-listbox"
aria-describedby="react-select-2-placeholder"
aria-expanded="true"
aria-haspopup="true"
aria-owns="react-select-2-listbox"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="react-select-2-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
/>
</div>
<div
class="theme-dark Select__menu css-1nmdiq5-menu"
id="react-select-2-listbox"
>
<div
class="Select__menu-list css-1n6sfyn-MenuList"
>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-2-option-0"
tabindex="-1"
>
1: default
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-2-option-1"
tabindex="-1"
>
2: non-default
</div>
</div>
</div>
</div>
</div>
`;
@@ -5,75 +5,56 @@
import "@testing-library/jest-dom/extend-expect";
import { HotbarRemoveCommand } from "../hotbar-remove-command";
import type { RenderResult } from "@testing-library/react";
import { fireEvent } from "@testing-library/react";
import React from "react";
import type { DiContainer } from "@ogre-tools/injectable";
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
import type { DiRender } from "../../test-utils/renderFor";
import { renderFor } from "../../test-utils/renderFor";
import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable";
import { ConfirmDialog } from "../../confirm-dialog";
import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import type { HotbarStore } from "../../../../common/hotbars/store";
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
const mockHotbars: Partial<Record<string, any>> = {
"1": {
id: "1",
name: "Default",
items: [],
},
};
import hotbarsStateInjectable from "../../../../features/hotbar/storage/common/state.injectable";
import type { CreateHotbar } from "../../../../features/hotbar/storage/common/create-hotbar.injectable";
import createHotbarInjectable from "../../../../features/hotbar/storage/common/create-hotbar.injectable";
import type { IComputedValue } from "mobx";
import type { Hotbar } from "../../../../features/hotbar/storage/common/hotbar";
import hotbarsInjectable from "../../../../features/hotbar/storage/common/hotbars.injectable";
describe("<HotbarRemoveCommand />", () => {
let di: DiContainer;
let render: DiRender;
let result: RenderResult;
let createHotbar: CreateHotbar;
let hotbars: IComputedValue<Hotbar[]>;
beforeEach(() => {
di = getDiForUnitTesting();
const di = getDiForUnitTesting();
const render = renderFor(di);
di.override(storesAndApisCanBeCreatedInjectable, () => true);
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
render = renderFor(di);
});
createHotbar = di.inject(createHotbarInjectable);
hotbars = di.inject(hotbarsInjectable);
it("renders w/o errors", () => {
di.override(hotbarStoreInjectable, () => ({
hotbars: [mockHotbars["1"]],
getById: (id: string) => mockHotbars[id],
remove: () => {
},
hotbarIndex: () => 0,
getDisplayLabel: () => "1: Default",
}) as unknown as HotbarStore);
const hotbarsState = di.inject(hotbarsStateInjectable);
const defaultHotbar = createHotbar({ name: "default" });
const nonDefaultHotbar = createHotbar({ name: "non-default" });
const { container } = render(<HotbarRemoveCommand />);
hotbarsState.set(defaultHotbar.id, defaultHotbar);
hotbarsState.set(nonDefaultHotbar.id, nonDefaultHotbar);
expect(container).toBeInstanceOf(HTMLElement);
});
it("calls remove if you click on the entry", () => {
const removeMock = jest.fn();
di.override(hotbarStoreInjectable, () => ({
hotbars: [mockHotbars["1"]],
getById: (id: string) => mockHotbars[id],
remove: removeMock,
hotbarIndex: () => 0,
getDisplayLabel: () => "1: Default",
}) as unknown as HotbarStore);
const { getByText } = render(
result = render((
<>
<HotbarRemoveCommand />
<ConfirmDialog />
</>,
);
</>
));
});
fireEvent.click(getByText("1: Default"));
fireEvent.click(getByText("Remove Hotbar"));
it("renders w/o errors", () => {
expect(result.container).toMatchSnapshot();
});
expect(removeMock).toHaveBeenCalled();
it("calls remove if you click on the entry", () => {
fireEvent.click(result.getByText("1: default"));
fireEvent.click(result.getByText("Remove Hotbar"));
expect(hotbars.get().length).toBe(1);
});
});
@@ -7,15 +7,15 @@ import React from "react";
import { observer } from "mobx-react";
import type { InputValidator } from "../input";
import { Input } from "../input";
import type { CreateHotbarData, CreateHotbarOptions } from "../../../common/hotbars/types";
import { withInjectables } from "@ogre-tools/injectable-react";
import commandOverlayInjectable from "../command-palette/command-overlay.injectable";
import uniqueHotbarNameInjectable from "../input/validators/unique-hotbar-name.injectable";
import addHotbarInjectable from "../../../common/hotbars/add-hotbar.injectable";
import type { AddHotbar } from "../../../features/hotbar/storage/common/add.injectable";
import addHotbarInjectable from "../../../features/hotbar/storage/common/add.injectable";
interface Dependencies {
closeCommandOverlay: () => void;
addHotbar: (data: CreateHotbarData, opts: CreateHotbarOptions) => void;
addHotbar: AddHotbar;
uniqueHotbarName: InputValidator<boolean>;
}
@@ -5,7 +5,7 @@
import "./hotbar-menu.scss";
import React from "react";
import React, { useState } from "react";
import { observer } from "mobx-react";
import { HotbarEntityIcon } from "./hotbar-entity-icon";
import type { IClassName } from "@k8slens/utilities";
@@ -16,57 +16,46 @@ import { DragDropContext, Draggable, Droppable, type DropResult } from "react-be
import { HotbarSelector } from "./hotbar-selector";
import { HotbarCell } from "./hotbar-cell";
import { HotbarIcon } from "./hotbar-icon";
import type { HotbarItem } from "../../../common/hotbars/types";
import { defaultHotbarCells } from "../../../common/hotbars/types";
import { action, makeObservable, observable } from "mobx";
import hotbarStoreInjectable from "../../../common/hotbars/store.injectable";
import type { HotbarItem } from "../../../features/hotbar/storage/common/types";
import { defaultHotbarCells } from "../../../features/hotbar/storage/common/types";
import type { IComputedValue } from "mobx";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { HotbarStore } from "../../../common/hotbars/store";
import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable";
import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar";
import activeHotbarInjectable from "../../../features/hotbar/storage/common/active.injectable";
export interface HotbarMenuProps {
className?: IClassName;
}
interface Dependencies {
hotbarStore: HotbarStore;
activeHotbar: IComputedValue<Hotbar | undefined>;
entityRegistry: CatalogEntityRegistry;
}
@observer
class NonInjectedHotbarMenu extends React.Component<Dependencies & HotbarMenuProps> {
@observable draggingOver = false;
const NonInjectedHotbarMenu = observer((props: Dependencies & HotbarMenuProps) => {
const {
activeHotbar,
entityRegistry,
className,
} = props;
constructor(props: Dependencies & HotbarMenuProps) {
super(props);
makeObservable(this);
}
const [draggingOver, setDraggingOver] = useState(false);
const hotbar = activeHotbar.get();
get hotbar() {
return this.props.hotbarStore.getActive();
}
getEntity(item: HotbarItem | null) {
const hotbar = this.props.hotbarStore.getActive();
if (!hotbar || !item) {
return null;
const getEntity = (item: HotbarItem | null) => {
if (!item) {
return undefined;
}
return this.props.entityRegistry.getById(item.entity.uid) ?? null;
}
return entityRegistry.getById(item.entity.uid);
};
const onDragStart = () => setDraggingOver(true);
const onDragEnd = (result: DropResult) => {
setDraggingOver(false);
@action
onDragStart() {
this.draggingOver = true;
}
@action
onDragEnd(result: DropResult) {
const { source, destination } = result;
this.draggingOver = false;
if (!destination) { // Dropped outside of the list
return;
}
@@ -74,135 +63,115 @@ class NonInjectedHotbarMenu extends React.Component<Dependencies & HotbarMenuPro
const from = parseInt(source.droppableId);
const to = parseInt(destination.droppableId);
this.props.hotbarStore.restackItems(from, to);
}
removeItem = (uid: string) => {
const hotbar = this.props.hotbarStore;
hotbar.removeFromHotbar(uid);
hotbar?.restack(from, to);
};
addItem = (entity: CatalogEntity, index = -1) => {
const hotbar = this.props.hotbarStore;
hotbar.addToHotbar(entity, index);
const removeItem = (entityId: string) => {
hotbar?.removeEntity(entityId);
};
getMoveAwayDirection(entityId: string | undefined | null, cellIndex: number) {
if (!entityId) {
const addItem = (entity: CatalogEntity) => {
hotbar?.addEntity(entity);
};
const getMoveAwayDirection = (entityId: string | undefined | null, cellIndex: number) => {
if (!entityId || !hotbar) {
return "animateDown";
}
const draggableItemIndex = this.hotbar.items.findIndex(item => item?.entity.uid == entityId);
const draggableItemIndex = hotbar.items.findIndex(item => item?.entity.uid == entityId);
return draggableItemIndex > cellIndex ? "animateDown" : "animateUp";
}
};
renderGrid() {
return this.hotbar.items.map((item, index) => {
const entity = this.getEntity(item);
return (
<Droppable droppableId={`${index}`} key={index}>
{(provided, snapshot) => (
<HotbarCell
index={index}
key={entity ? entity.getId() : `cell${index}`}
innerRef={provided.innerRef}
className={cssNames(
{
isDraggingOver: snapshot.isDraggingOver,
isDraggingOwner: snapshot.draggingOverWith == entity?.getId(),
},
this.getMoveAwayDirection(snapshot.draggingOverWith, index),
)}
{...provided.droppableProps}
>
{item && (
<Draggable
draggableId={item.entity.uid}
key={item.entity.uid}
index={0}
>
{(provided, snapshot) => {
const style = {
zIndex: defaultHotbarCells - index,
position: "absolute",
...provided.draggableProps.style,
} as React.CSSProperties;
return (
<div
key={item.entity.uid}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={style}
>
{entity ? (
<HotbarEntityIcon
key={index}
index={index}
entity={entity}
onClick={() => this.props.entityRegistry.onRun(entity)}
className={cssNames({ isDragging: snapshot.isDragging })}
remove={this.removeItem}
add={this.addItem}
size={40}
/>
) : (
<HotbarIcon
uid={`hotbar-icon-${item.entity.uid}`}
title={item.entity.name}
source={item.entity.source ?? "local"}
tooltip={`${item.entity.name} (${item.entity.source})`}
menuItems={[
{
title: "Remove from Hotbar",
onClick: () => this.removeItem(item.entity.uid),
},
]}
disabled
size={40}
/>
)}
</div>
);
}}
</Draggable>
)}
{provided.placeholder}
</HotbarCell>
)}
</Droppable>
);
});
}
render() {
const { className, hotbarStore } = this.props;
const hotbar = hotbarStore.getActive();
const renderGrid = () => hotbar?.items.map((item, index) => {
const entity = getEntity(item);
return (
<div className={cssNames("HotbarMenu flex column", { draggingOver: this.draggingOver }, className)}>
<div className="HotbarItems flex column gaps">
<DragDropContext
onDragStart={() => this.onDragStart()}
onDragEnd={(result) => this.onDragEnd(result)}>
{this.renderGrid()}
</DragDropContext>
</div>
<HotbarSelector hotbar={hotbar} />
</div>
<Droppable droppableId={`${index}`} key={index}>
{(provided, snapshot) => (
<HotbarCell
index={index}
key={entity ? entity.getId() : `cell${index}`}
innerRef={provided.innerRef}
className={cssNames(
{
isDraggingOver: snapshot.isDraggingOver,
isDraggingOwner: snapshot.draggingOverWith == entity?.getId(),
},
getMoveAwayDirection(snapshot.draggingOverWith, index),
)}
{...provided.droppableProps}
>
{item && (
<Draggable
draggableId={item.entity.uid}
key={item.entity.uid}
index={0}
>
{(provided, snapshot) => (
<div
key={item.entity.uid}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
zIndex: defaultHotbarCells - index,
position: "absolute" as const,
...provided.draggableProps.style,
}}
>
{entity ? (
<HotbarEntityIcon
key={index}
index={index}
entity={entity}
onClick={() => entityRegistry.onRun(entity)}
className={cssNames({ isDragging: snapshot.isDragging })}
remove={removeItem}
add={addItem}
size={40} />
) : (
<HotbarIcon
uid={`hotbar-icon-${item.entity.uid}`}
title={item.entity.name}
source={item.entity.source ?? "local"}
tooltip={`${item.entity.name} (${item.entity.source})`}
menuItems={[
{
title: "Remove from Hotbar",
onClick: () => removeItem(item.entity.uid),
},
]}
disabled
size={40} />
)}
</div>
)}
</Draggable>
)}
{provided.placeholder}
</HotbarCell>
)}
</Droppable>
);
}
}
});
return (
<div className={cssNames("HotbarMenu flex column", { draggingOver }, className)}>
<div className="HotbarItems flex column gaps">
<DragDropContext
onDragStart={() => onDragStart()}
onDragEnd={(result) => onDragEnd(result)}>
{renderGrid()}
</DragDropContext>
</div>
<HotbarSelector />
</div>
);
});
export const HotbarMenu = withInjectables<Dependencies, HotbarMenuProps>(NonInjectedHotbarMenu, {
getProps: (di, props) => ({
...props,
hotbarStore: di.inject(hotbarStoreInjectable),
entityRegistry: di.inject(catalogEntityRegistryInjectable),
activeHotbar: di.inject(activeHotbarInjectable),
}),
});
@@ -6,23 +6,32 @@
import React from "react";
import { observer } from "mobx-react";
import { Select } from "../select";
import hotbarStoreInjectable from "../../../common/hotbars/store.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import commandOverlayInjectable from "../command-palette/command-overlay.injectable";
import type { HotbarStore } from "../../../common/hotbars/store";
import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable";
import openConfirmDialogInjectable from "../confirm-dialog/open.injectable";
import type { IComputedValue } from "mobx";
import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar";
import type { ComputeHotbarDisplayLabel } from "../../../features/hotbar/storage/common/compute-display-label.injectable";
import computeHotbarDisplayLabelInjectable from "../../../features/hotbar/storage/common/compute-display-label.injectable";
import hotbarsInjectable from "../../../features/hotbar/storage/common/hotbars.injectable";
import type { RemoveHotbar } from "../../../features/hotbar/storage/common/remove.injectable";
import removeHotbarInjectable from "../../../features/hotbar/storage/common/remove.injectable";
interface Dependencies {
closeCommandOverlay: () => void;
openConfirmDialog: OpenConfirmDialog;
hotbarStore: HotbarStore;
hotbars: IComputedValue<Hotbar[]>;
computeHotbarDisplayLabel: ComputeHotbarDisplayLabel;
removeHotbar: RemoveHotbar;
}
const NonInjectedHotbarRemoveCommand = observer(({
closeCommandOverlay,
hotbarStore,
openConfirmDialog,
hotbars,
computeHotbarDisplayLabel,
removeHotbar,
}: Dependencies) => (
<Select
menuPortalTarget={null}
@@ -38,28 +47,28 @@ const NonInjectedHotbarRemoveCommand = observer(({
primary: false,
accent: true,
},
ok: () => hotbarStore.remove(option.value),
ok: () => removeHotbar(option.value),
message: (
<div className="confirm flex column gaps">
<p>
Are you sure you want remove hotbar
{" "}
<b>
{option.value.name}
{option.value.name.get()}
</b>
?
</p>
</div>
),
});
} }
}}
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
menuIsOpen={true}
options={(
hotbarStore.hotbars
hotbars.get()
.map(hotbar => ({
value: hotbar,
label: hotbarStore.getDisplayLabel(hotbar),
label: computeHotbarDisplayLabel(hotbar),
}))
)}
autoFocus={true}
@@ -72,7 +81,9 @@ export const HotbarRemoveCommand = withInjectables<Dependencies>(NonInjectedHotb
getProps: (di, props) => ({
...props,
closeCommandOverlay: di.inject(commandOverlayInjectable).close,
hotbarStore: di.inject(hotbarStoreInjectable),
openConfirmDialog: di.inject(openConfirmDialogInjectable),
computeHotbarDisplayLabel: di.inject(computeHotbarDisplayLabelInjectable),
hotbars: di.inject(hotbarsInjectable),
removeHotbar: di.inject(removeHotbarInjectable),
}),
});
@@ -6,36 +6,46 @@
import React, { useState } from "react";
import { observer } from "mobx-react";
import { Select } from "../select";
import hotbarStoreInjectable from "../../../common/hotbars/store.injectable";
import type { InputValidator } from "../input";
import { Input } from "../input";
import { withInjectables } from "@ogre-tools/injectable-react";
import commandOverlayInjectable from "../command-palette/command-overlay.injectable";
import uniqueHotbarNameInjectable from "../input/validators/unique-hotbar-name.injectable";
import type { HotbarStore } from "../../../common/hotbars/store";
import type { IComputedValue } from "mobx";
import { action } from "mobx";
import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar";
import type { GetHotbarById } from "../../../features/hotbar/storage/common/get-by-id.injectable";
import getHotbarByIdInjectable from "../../../features/hotbar/storage/common/get-by-id.injectable";
import hotbarsInjectable from "../../../features/hotbar/storage/common/hotbars.injectable";
import type { ComputeHotbarDisplayLabel } from "../../../features/hotbar/storage/common/compute-display-label.injectable";
import computeHotbarDisplayLabelInjectable from "../../../features/hotbar/storage/common/compute-display-label.injectable";
interface Dependencies {
closeCommandOverlay: () => void;
hotbarStore: HotbarStore;
getHotbarById: GetHotbarById;
computeHotbarDisplayLabel: ComputeHotbarDisplayLabel;
uniqueHotbarName: InputValidator<false>;
hotbars: IComputedValue<Hotbar[]>;
}
const NonInjectedHotbarRenameCommand = observer(({
closeCommandOverlay,
hotbarStore,
getHotbarById,
computeHotbarDisplayLabel,
uniqueHotbarName,
hotbars,
}: Dependencies) => {
const [hotbarId, setHotbarId] = useState("");
const [hotbarName, setHotbarName] = useState("");
const onSubmit = (name: string) => {
const onSubmit = action((name: string) => {
if (!name.trim()) {
return;
}
hotbarStore.setHotbarName(hotbarId, name);
getHotbarById(hotbarId)?.name.set(name);
closeCommandOverlay();
};
});
if (hotbarId) {
return (
@@ -65,16 +75,16 @@ const NonInjectedHotbarRenameCommand = observer(({
onChange={(option) => {
if (option) {
setHotbarId(option.value.id);
setHotbarName(option.value.name);
setHotbarName(option.value.name.get());
}
}}
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
menuIsOpen={true}
options={(
hotbarStore.hotbars
hotbars.get()
.map(hotbar => ({
value: hotbar,
label: hotbarStore.getDisplayLabel(hotbar),
label: computeHotbarDisplayLabel(hotbar),
}))
)}
autoFocus={true}
@@ -86,9 +96,11 @@ const NonInjectedHotbarRenameCommand = observer(({
export const HotbarRenameCommand = withInjectables<Dependencies>(NonInjectedHotbarRenameCommand, {
getProps: (di, props) => ({
closeCommandOverlay: di.inject(commandOverlayInjectable).close,
hotbarStore: di.inject(hotbarStoreInjectable),
uniqueHotbarName: di.inject(uniqueHotbarNameInjectable),
...props,
closeCommandOverlay: di.inject(commandOverlayInjectable).close,
uniqueHotbarName: di.inject(uniqueHotbarNameInjectable),
getHotbarById: di.inject(getHotbarByIdInjectable),
hotbars: di.inject(hotbarsInjectable),
computeHotbarDisplayLabel: di.inject(computeHotbarDisplayLabelInjectable),
}),
});
@@ -7,28 +7,40 @@ import styles from "./hotbar-selector.module.scss";
import React, { useRef, useState } from "react";
import { Icon } from "../icon";
import { Badge } from "../badge";
import hotbarStoreInjectable from "../../../common/hotbars/store.injectable";
import { HotbarSwitchCommand } from "./hotbar-switch-command";
import { Tooltip, TooltipPosition } from "../tooltip";
import { observer } from "mobx-react";
import type { Hotbar } from "../../../common/hotbars/types";
import { withInjectables } from "@ogre-tools/injectable-react";
import commandOverlayInjectable from "../command-palette/command-overlay.injectable";
import { cssNames } from "@k8slens/utilities";
import type { HotbarStore } from "../../../common/hotbars/store";
import type { IComputedValue } from "mobx";
import activeHotbarInjectable from "../../../features/hotbar/storage/common/active.injectable";
import type { SwitchToPreviousHotbar } from "../../../features/hotbar/storage/common/switch-to-previous.injectable";
import type { SwitchToNextHotbar } from "../../../features/hotbar/storage/common/switch-to-next.injectable";
import switchToNextHotbarInjectable from "../../../features/hotbar/storage/common/switch-to-next.injectable";
import switchToPreviousHotbarInjectable from "../../../features/hotbar/storage/common/switch-to-previous.injectable";
import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar";
import type { ComputeDisplayIndex } from "../../../features/hotbar/storage/common/compute-display-index.injectable";
import computeDisplayIndexInjectable from "../../../features/hotbar/storage/common/compute-display-index.injectable";
interface Dependencies {
hotbarStore: HotbarStore;
activeHotbar: IComputedValue<Hotbar | undefined>;
openCommandOverlay: (component: React.ReactElement) => void;
switchToPreviousHotbar: SwitchToPreviousHotbar;
switchToNextHotbar: SwitchToNextHotbar;
computeDisplayIndex: ComputeDisplayIndex;
}
export interface HotbarSelectorProps extends Partial<Dependencies> {
hotbar: Hotbar;
}
const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOverlay }: HotbarSelectorProps & Dependencies) => {
const NonInjectedHotbarSelector = observer(({
activeHotbar,
openCommandOverlay,
switchToNextHotbar,
switchToPreviousHotbar,
computeDisplayIndex,
}: Dependencies) => {
const [tooltipVisible, setTooltipVisible] = useState(false);
const tooltipTimeout = useRef<number>();
const hotbar = activeHotbar.get();
function clearTimer() {
clearTimeout(tooltipTimeout.current);
@@ -42,12 +54,12 @@ const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOv
function onPrevClick() {
onTooltipShow();
hotbarStore.switchToPrevious();
switchToPreviousHotbar();
}
function onNextClick() {
onTooltipShow();
hotbarStore.switchToNext();
switchToNextHotbar();
}
function onMouseEvent(event: React.MouseEvent) {
@@ -59,13 +71,13 @@ const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOv
<div className={styles.HotbarSelector}>
<Icon
material="arrow_left"
className={cssNames(styles.Icon, styles.previous)}
className={cssNames(styles.Icon)}
onClick={onPrevClick}/>
<div className={styles.HotbarIndex}>
<Badge
id="hotbarIndex"
small
label={hotbarStore.getDisplayIndex(hotbarStore.getActive())}
label={hotbar ? computeDisplayIndex(hotbar.id) : "??"}
onClick={() => openCommandOverlay(<HotbarSwitchCommand />)}
className={styles.Badge}
onMouseEnter={onMouseEvent}
@@ -76,7 +88,7 @@ const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOv
targetId="hotbarIndex"
preferredPositions={[TooltipPosition.TOP, TooltipPosition.TOP_LEFT]}
>
{hotbar.name}
{hotbar?.name}
</Tooltip>
</div>
<Icon
@@ -88,10 +100,13 @@ const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOv
);
});
export const HotbarSelector = withInjectables<Dependencies, HotbarSelectorProps>(NonInjectedHotbarSelector, {
export const HotbarSelector = withInjectables<Dependencies>(NonInjectedHotbarSelector, {
getProps: (di, props) => ({
hotbarStore: di.inject(hotbarStoreInjectable),
openCommandOverlay: di.inject(commandOverlayInjectable).open,
...props,
openCommandOverlay: di.inject(commandOverlayInjectable).open,
activeHotbar: di.inject(activeHotbarInjectable),
switchToNextHotbar: di.inject(switchToNextHotbarInjectable),
switchToPreviousHotbar: di.inject(switchToPreviousHotbarInjectable),
computeDisplayIndex: di.inject(computeDisplayIndexInjectable),
}),
});
@@ -6,21 +6,28 @@
import React from "react";
import { observer } from "mobx-react";
import { Select } from "../select";
import hotbarStoreInjectable from "../../../common/hotbars/store.injectable";
import type { CommandOverlay } from "../command-palette";
import { HotbarAddCommand } from "./hotbar-add-command";
import { HotbarRemoveCommand } from "./hotbar-remove-command";
import { HotbarRenameCommand } from "./hotbar-rename-command";
import { withInjectables } from "@ogre-tools/injectable-react";
import commandOverlayInjectable from "../command-palette/command-overlay.injectable";
import type { HotbarStore } from "../../../common/hotbars/store";
import type { SetAsActiveHotbar } from "../../../features/hotbar/storage/common/set-as-active.injectable";
import type { IComputedValue } from "mobx";
import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar";
import type { ComputeHotbarDisplayLabel } from "../../../features/hotbar/storage/common/compute-display-label.injectable";
import computeHotbarDisplayLabelInjectable from "../../../features/hotbar/storage/common/compute-display-label.injectable";
import hotbarsInjectable from "../../../features/hotbar/storage/common/hotbars.injectable";
import setAsActiveHotbarInjectable from "../../../features/hotbar/storage/common/set-as-active.injectable";
const hotbarAddAction = Symbol("hotbar-add");
const hotbarRemoveAction = Symbol("hotbar-remove");
const hotbarRenameAction = Symbol("hotbar-rename");
interface Dependencies {
hotbarStore: HotbarStore;
setAsActiveHotbar: SetAsActiveHotbar;
computeHotbarDisplayLabel: ComputeHotbarDisplayLabel;
hotbars: IComputedValue<Hotbar[]>;
commandOverlay: CommandOverlay;
}
@@ -29,7 +36,9 @@ function ignoreIf<T>(check: boolean, menuItems: T[]): T[] {
}
const NonInjectedHotbarSwitchCommand = observer(({
hotbarStore,
setAsActiveHotbar,
computeHotbarDisplayLabel,
hotbars,
commandOverlay,
}: Dependencies) => (
<Select
@@ -50,22 +59,22 @@ const NonInjectedHotbarSwitchCommand = observer(({
return commandOverlay.open(<HotbarRenameCommand />);
}
} else {
hotbarStore.setActiveHotbar(option.value);
setAsActiveHotbar(option.value);
commandOverlay.close();
}
}}
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
menuIsOpen={true}
options={[
...hotbarStore.hotbars.map(hotbar => ({
...hotbars.get().map(hotbar => ({
value: hotbar,
label: hotbarStore.getDisplayLabel(hotbar),
label: computeHotbarDisplayLabel(hotbar),
})),
{
value: hotbarAddAction,
label: "Add hotbar ...",
},
...ignoreIf(hotbarStore.hotbars.length > 1, [
...ignoreIf(hotbars.get().length > 1, [
{
value: hotbarRemoveAction,
label: "Remove hotbar ...",
@@ -85,8 +94,10 @@ const NonInjectedHotbarSwitchCommand = observer(({
export const HotbarSwitchCommand = withInjectables<Dependencies>(NonInjectedHotbarSwitchCommand, {
getProps: (di, props) => ({
hotbarStore: di.inject(hotbarStoreInjectable),
commandOverlay: di.inject(commandOverlayInjectable),
...props,
commandOverlay: di.inject(commandOverlayInjectable),
computeHotbarDisplayLabel: di.inject(computeHotbarDisplayLabelInjectable),
hotbars: di.inject(hotbarsInjectable),
setAsActiveHotbar: di.inject(setAsActiveHotbarInjectable),
}),
});