mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
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:
1 parent
ba4a283af9
commit
49f0a1af9c
365 files changed
+6733
-3972
No files matched your search
+97
@@ -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>
|
||||
`;
|
||||
+30
-49
@@ -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),
|
||||
}),
|
||||
});
|
||||
Reference in new issue
Block a user