mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Restructure to monorepo (#6907)
* wip: restructure to monorepo Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * refactor create-release-pr to a package Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * build fixes Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * github workflow fixes Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix typo Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * add webpack-env types to core Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix github workflows Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * refactor/fix integration tests Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * lint fix Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * yarn run dev Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * eslint settings for vscode Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * move templates to right package Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * open-lens build fixes Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * integration test fix Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix nx task dependencies Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * use bash shell for unit tests in test workflow Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix test:unit for windows Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix win-ca webpack error in open-lens Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix win-ca webpack error in open-lens Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix build:app on windows Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * remove ELECTRON_BUILDER_EXTRA_ARGS Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * sync src/ from master Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * remove Makefile from core Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
0066ca4ea2
commit
2657df2293
3321 files changed
+28699
-11447
No files matched your search
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
import { HotbarRemoveCommand } from "../hotbar-remove-command";
|
||||
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: [],
|
||||
},
|
||||
};
|
||||
|
||||
describe("<HotbarRemoveCommand />", () => {
|
||||
let di: DiContainer;
|
||||
let render: DiRender;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
||||
|
||||
render = renderFor(di);
|
||||
});
|
||||
|
||||
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 { container } = render(<HotbarRemoveCommand />);
|
||||
|
||||
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(
|
||||
<>
|
||||
<HotbarRemoveCommand />
|
||||
<ConfirmDialog />
|
||||
</>,
|
||||
);
|
||||
|
||||
fireEvent.click(getByText("1: Default"));
|
||||
fireEvent.click(getByText("Remove Hotbar"));
|
||||
|
||||
expect(removeMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
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";
|
||||
|
||||
interface Dependencies {
|
||||
closeCommandOverlay: () => void;
|
||||
addHotbar: (data: CreateHotbarData, opts: CreateHotbarOptions) => void;
|
||||
uniqueHotbarName: InputValidator<boolean>;
|
||||
}
|
||||
|
||||
const NonInjectedHotbarAddCommand = observer(({ closeCommandOverlay, addHotbar, uniqueHotbarName }: Dependencies) => {
|
||||
const onSubmit = (name: string) => {
|
||||
if (!name.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
addHotbar({ name }, { setActive: true });
|
||||
closeCommandOverlay();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
placeholder="Hotbar name"
|
||||
autoFocus={true}
|
||||
theme="round-black"
|
||||
data-test-id="command-palette-hotbar-add-name"
|
||||
validators={uniqueHotbarName}
|
||||
onSubmit={onSubmit}
|
||||
dirty={true}
|
||||
showValidationLine={true}
|
||||
/>
|
||||
<small className="hint">
|
||||
Please provide a new hotbar name (Press "Enter" to confirm or "Escape" to cancel)
|
||||
</small>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export const HotbarAddCommand = withInjectables<Dependencies>(NonInjectedHotbarAddCommand, {
|
||||
getProps: (di, props) => ({
|
||||
closeCommandOverlay: di.inject(commandOverlayInjectable).close,
|
||||
addHotbar: di.inject(addHotbarInjectable),
|
||||
uniqueHotbarName: di.inject(uniqueHotbarNameInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import "./hotbar-menu.scss";
|
||||
|
||||
import type { HTMLAttributes, ReactNode } from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { cssNames } from "../../utils";
|
||||
|
||||
export interface HotbarCellProps extends HTMLAttributes<HTMLDivElement> {
|
||||
children?: ReactNode;
|
||||
index: number;
|
||||
innerRef?: React.Ref<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export function HotbarCell({ innerRef, children, className, ...rest }: HotbarCellProps) {
|
||||
const [animating, setAnimating] = useState(false);
|
||||
const onAnimationEnd = () => { setAnimating(false); };
|
||||
const onClick = () => {
|
||||
setAnimating(!className?.includes("isDraggingOver"));
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cssNames("HotbarCell", { animating }, className)}
|
||||
onAnimationEnd={onAnimationEnd}
|
||||
onClick={onClick}
|
||||
ref={innerRef}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.led {
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
top: 3px;
|
||||
background-color: var(--layoutBackground);
|
||||
border: 1px solid var(--clusterMenuBackground);
|
||||
border-radius: 50%;
|
||||
padding: 0px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
|
||||
&.online {
|
||||
background-color: var(--primary);
|
||||
box-shadow: 0 0 5px var(--clusterMenuBackground), 0 0 5px var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
right: -2px;
|
||||
bottom: -3px;
|
||||
margin: -8px;
|
||||
font-size: var(--font-size-small);
|
||||
background: var(--clusterMenuBackground);
|
||||
color: var(--textColorAccent);
|
||||
padding: 0px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--clusterMenuBackground);
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
|
||||
svg {
|
||||
width: 13px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import styles from "./hotbar-entity-icon.module.scss";
|
||||
|
||||
import React from "react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import type { CatalogCategoryRegistry, CatalogEntity, CatalogEntityContextMenu } from "../../../common/catalog";
|
||||
import type { IClassName } from "../../utils";
|
||||
import { cssNames } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { HotbarIcon } from "./hotbar-icon";
|
||||
import { LensKubernetesClusterStatus } from "../../../common/catalog-entities/kubernetes-cluster";
|
||||
import type { VisitEntityContextMenu } from "../../../common/catalog/visit-entity-context-menu.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable";
|
||||
import visitEntityContextMenuInjectable from "../../../common/catalog/visit-entity-context-menu.injectable";
|
||||
import activeEntityInjectable from "../../api/catalog/entity/active.injectable";
|
||||
import type { Navigate } from "../../navigation/navigate.injectable";
|
||||
import navigateInjectable from "../../navigation/navigate.injectable";
|
||||
|
||||
export interface HotbarEntityIconProps {
|
||||
entity: CatalogEntity;
|
||||
index: number;
|
||||
errorClass?: IClassName;
|
||||
add: (item: CatalogEntity, index: number) => void;
|
||||
remove: (uid: string) => void;
|
||||
size?: number;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
visitEntityContextMenu: VisitEntityContextMenu;
|
||||
catalogCategoryRegistry: CatalogCategoryRegistry;
|
||||
activeEntity: IComputedValue<CatalogEntity | undefined>;
|
||||
navigate: Navigate;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedHotbarEntityIcon extends React.Component<HotbarEntityIconProps & Dependencies> {
|
||||
private readonly menuItems = observable.array<CatalogEntityContextMenu>();
|
||||
|
||||
private renderKindIcon() {
|
||||
const className = styles.badge;
|
||||
const category = this.props.catalogCategoryRegistry.getCategoryForEntity(this.props.entity);
|
||||
|
||||
if (!category) {
|
||||
return <Icon material="bug_report" className={className} />;
|
||||
}
|
||||
|
||||
if (Icon.isSvg(category.metadata.icon)) {
|
||||
return <Icon svg={category.metadata.icon} className={className} />;
|
||||
}
|
||||
|
||||
return <Icon material={category.metadata.icon} className={className} />;
|
||||
}
|
||||
|
||||
private renderLedIcon() {
|
||||
if (this.props.entity.kind !== "KubernetesCluster") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cssNames(styles.led, {
|
||||
// TODO: make it more generic
|
||||
[styles.online]: this.props.entity.status.phase === LensKubernetesClusterStatus.CONNECTED,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
isActive(item: CatalogEntity) {
|
||||
return this.props.activeEntity.get()?.metadata?.uid == item.getId();
|
||||
}
|
||||
|
||||
onMenuOpen() {
|
||||
this.menuItems.replace([{
|
||||
title: "Remove from Hotbar",
|
||||
onClick: () => this.props.remove(this.props.entity.getId()),
|
||||
}]);
|
||||
|
||||
this.props.visitEntityContextMenu(this.props.entity, {
|
||||
menuItems: this.menuItems,
|
||||
navigate: this.props.navigate,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { entity, className, onClick } = this.props;
|
||||
|
||||
return (
|
||||
<HotbarIcon
|
||||
uid={entity.getId()}
|
||||
title={entity.getName()}
|
||||
source={entity.metadata.source}
|
||||
src={entity.spec.icon?.src}
|
||||
material={entity.spec.icon?.material}
|
||||
background={entity.spec.icon?.background}
|
||||
className={className}
|
||||
active={this.isActive(entity)}
|
||||
onMenuOpen={() => this.onMenuOpen()}
|
||||
disabled={!entity}
|
||||
menuItems={this.menuItems}
|
||||
tooltip={(
|
||||
entity.metadata.source
|
||||
? `${entity.getName()} (${entity.metadata.source})`
|
||||
: entity.getName()
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{this.renderLedIcon()}
|
||||
{this.renderKindIcon()}
|
||||
</HotbarIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const HotbarEntityIcon = withInjectables<Dependencies, HotbarEntityIconProps>(NonInjectedHotbarEntityIcon, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
catalogCategoryRegistry: di.inject(catalogCategoryRegistryInjectable),
|
||||
visitEntityContextMenu: di.inject(visitEntityContextMenuInjectable),
|
||||
activeEntity: di.inject(activeEntityInjectable),
|
||||
navigate: di.inject(navigateInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.HotbarIcon {
|
||||
--corner: 0px 0px 0px 1px var(--clusterMenuBackground);
|
||||
--iconActiveShadow: var(--corner), var(--corner), 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px var(--textColorAccent);
|
||||
--iconHoverShadow: 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px #ffffff50;
|
||||
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border-radius: 6px;
|
||||
transition: box-shadow 0.1s ease-in-out;
|
||||
|
||||
&:not(.active):hover {
|
||||
box-shadow: var(--iconHoverShadow);
|
||||
}
|
||||
}
|
||||
|
||||
.contextMenuAvailable {
|
||||
cursor: context-menu;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 6px;
|
||||
|
||||
&.hasImage {
|
||||
background-color: var(--clusterMenuCellBackground);
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
box-shadow: var(--iconActiveShadow);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import styles from "./hotbar-icon.module.scss";
|
||||
|
||||
import React, { useState } from "react";
|
||||
|
||||
import type { CatalogEntityContextMenu } from "../../../common/catalog";
|
||||
import { cssNames } from "../../utils";
|
||||
import { Menu, MenuItem } from "../menu";
|
||||
import { observer } from "mobx-react";
|
||||
import type { AvatarProps } from "../avatar";
|
||||
import { Avatar } from "../avatar";
|
||||
import { Icon } from "../icon";
|
||||
import { Tooltip } from "../tooltip";
|
||||
import type { NormalizeCatalogEntityContextMenu } from "../../catalog/normalize-menu-item.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import normalizeCatalogEntityContextMenuInjectable from "../../catalog/normalize-menu-item.injectable";
|
||||
|
||||
export interface HotbarIconProps extends AvatarProps {
|
||||
uid: string;
|
||||
source?: string;
|
||||
material?: string;
|
||||
onMenuOpen?: () => void;
|
||||
active?: boolean;
|
||||
menuItems?: CatalogEntityContextMenu[];
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
normalizeMenuItem: NormalizeCatalogEntityContextMenu;
|
||||
}
|
||||
|
||||
const NonInjectedHotbarIcon = observer(({
|
||||
menuItems = [],
|
||||
size = 40,
|
||||
tooltip,
|
||||
normalizeMenuItem,
|
||||
...props
|
||||
}: HotbarIconProps & Dependencies) => {
|
||||
const { uid, title, src, material, active, className, source, disabled, onMenuOpen, onClick, children, ...rest } = props;
|
||||
const id = `hotbarIcon-${uid}`;
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
const toggleMenu = () => {
|
||||
setMenuOpen(!menuOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cssNames(styles.HotbarIcon, className, { [styles.contextMenuAvailable]: menuItems.length > 0 })}>
|
||||
{tooltip && (
|
||||
<Tooltip targetId={id}>
|
||||
{tooltip}
|
||||
</Tooltip>
|
||||
)}
|
||||
<Avatar
|
||||
{...rest}
|
||||
id={id}
|
||||
title={title}
|
||||
colorHash={source ? `${title}-${source}` : title}
|
||||
className={cssNames(styles.avatar, { [styles.active]: active, [styles.hasImage]: !!src })}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
src={src}
|
||||
onClick={(event) => !disabled && onClick?.(event)}
|
||||
>
|
||||
{material && <Icon material={material} />}
|
||||
</Avatar>
|
||||
{children}
|
||||
<Menu
|
||||
usePortal
|
||||
htmlFor={id}
|
||||
isOpen={menuOpen}
|
||||
toggleEvent="contextmenu"
|
||||
position={{ right: true, bottom: true }} // FIXME: position does not work
|
||||
open={() => {
|
||||
onMenuOpen?.();
|
||||
toggleMenu();
|
||||
}}
|
||||
close={() => toggleMenu()}
|
||||
>
|
||||
{
|
||||
menuItems
|
||||
.map(normalizeMenuItem)
|
||||
.map((menuItem) => (
|
||||
<MenuItem key={menuItem.title} onClick={menuItem.onClick}>
|
||||
{menuItem.title}
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const HotbarIcon = withInjectables<Dependencies, HotbarIconProps>(NonInjectedHotbarIcon, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
normalizeMenuItem: di.inject(normalizeCatalogEntityContextMenuInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.HotbarMenu {
|
||||
$spacing: $padding * 2;
|
||||
|
||||
position: relative;
|
||||
text-align: center;
|
||||
background: var(--clusterMenuBackground);
|
||||
padding-top: 1px;
|
||||
width: var(--hotbar-width);
|
||||
overflow: hidden;
|
||||
|
||||
&.draggingOver::after {
|
||||
content: " ";
|
||||
position: fixed;
|
||||
left: var(--hotbar-width);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.HotbarItems {
|
||||
--cellWidth: 40px;
|
||||
--cellHeight: 40px;
|
||||
|
||||
box-sizing: content-box;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding-bottom: 8px;
|
||||
|
||||
&:hover {
|
||||
overflow: overlay;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.4em;
|
||||
background: transparent;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--borderFaintColor);
|
||||
}
|
||||
}
|
||||
|
||||
.HotbarCell {
|
||||
width: var(--cellWidth);
|
||||
height: var(--cellHeight);
|
||||
min-height: var(--cellHeight);
|
||||
margin: 12px;
|
||||
background: var(--clusterMenuCellBackground);
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
|
||||
&.isDraggingOver {
|
||||
background-color: #3e4148;
|
||||
box-shadow: 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px #ffffff30;
|
||||
|
||||
&:not(.isDraggingOwner) {
|
||||
z-index: 50;
|
||||
|
||||
> div:not(:empty) {
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 9px #00000042;
|
||||
}
|
||||
|
||||
&.animateUp {
|
||||
> div {
|
||||
transform: translate(0px, -40px)!important;
|
||||
}
|
||||
}
|
||||
|
||||
&.animateDown {
|
||||
> div {
|
||||
transform: translate(0px, 40px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.animating {
|
||||
&:empty {
|
||||
animation: shake .6s cubic-bezier(.36,.07,.19,.97) both;
|
||||
transform: translate3d(0, 0, 0);
|
||||
backface-visibility: hidden;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
&:not(:empty) {
|
||||
> div {
|
||||
animation: click .1s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
10%, 90% {
|
||||
transform: translate3d(-1px, 0, 0);
|
||||
}
|
||||
|
||||
20%, 80% {
|
||||
transform: translate3d(2px, 0, 0);
|
||||
}
|
||||
|
||||
30%, 50%, 70% {
|
||||
transform: translate3d(-4px, 0, 0);
|
||||
}
|
||||
|
||||
40%, 60% {
|
||||
transform: translate3d(4px, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes click {
|
||||
0% {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import "./hotbar-menu.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { HotbarEntityIcon } from "./hotbar-entity-icon";
|
||||
import type { IClassName } from "../../utils";
|
||||
import { cssNames } from "../../utils";
|
||||
import type { CatalogEntityRegistry } from "../../api/catalog/entity/registry";
|
||||
import type { CatalogEntity } from "../../api/catalog-entity";
|
||||
import { DragDropContext, Draggable, Droppable, type DropResult } from "react-beautiful-dnd";
|
||||
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 { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { HotbarStore } from "../../../common/hotbars/store";
|
||||
import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable";
|
||||
|
||||
export interface HotbarMenuProps {
|
||||
className?: IClassName;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
hotbarStore: HotbarStore;
|
||||
entityRegistry: CatalogEntityRegistry;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedHotbarMenu extends React.Component<Dependencies & HotbarMenuProps> {
|
||||
@observable draggingOver = false;
|
||||
|
||||
constructor(props: Dependencies & HotbarMenuProps) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
get hotbar() {
|
||||
return this.props.hotbarStore.getActive();
|
||||
}
|
||||
|
||||
getEntity(item: HotbarItem | null) {
|
||||
const hotbar = this.props.hotbarStore.getActive();
|
||||
|
||||
if (!hotbar || !item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.props.entityRegistry.getById(item.entity.uid) ?? null;
|
||||
}
|
||||
|
||||
@action
|
||||
onDragStart() {
|
||||
this.draggingOver = true;
|
||||
}
|
||||
|
||||
@action
|
||||
onDragEnd(result: DropResult) {
|
||||
const { source, destination } = result;
|
||||
|
||||
this.draggingOver = false;
|
||||
|
||||
if (!destination) { // Dropped outside of the list
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
addItem = (entity: CatalogEntity, index = -1) => {
|
||||
const hotbar = this.props.hotbarStore;
|
||||
|
||||
hotbar.addToHotbar(entity, index);
|
||||
};
|
||||
|
||||
getMoveAwayDirection(entityId: string | undefined | null, cellIndex: number) {
|
||||
if (!entityId) {
|
||||
return "animateDown";
|
||||
}
|
||||
|
||||
const draggableItemIndex = this.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();
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const HotbarMenu = withInjectables<Dependencies, HotbarMenuProps>(NonInjectedHotbarMenu, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
hotbarStore: di.inject(hotbarStoreInjectable),
|
||||
entityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
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";
|
||||
|
||||
interface Dependencies {
|
||||
closeCommandOverlay: () => void;
|
||||
openConfirmDialog: OpenConfirmDialog;
|
||||
hotbarStore: HotbarStore;
|
||||
}
|
||||
|
||||
const NonInjectedHotbarRemoveCommand = observer(({
|
||||
closeCommandOverlay,
|
||||
hotbarStore,
|
||||
openConfirmDialog,
|
||||
}: Dependencies) => (
|
||||
<Select
|
||||
menuPortalTarget={null}
|
||||
onChange={option => {
|
||||
if (!option) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeCommandOverlay();
|
||||
openConfirmDialog({
|
||||
okButtonProps: {
|
||||
label: "Remove Hotbar",
|
||||
primary: false,
|
||||
accent: true,
|
||||
},
|
||||
ok: () => hotbarStore.remove(option.value),
|
||||
message: (
|
||||
<div className="confirm flex column gaps">
|
||||
<p>
|
||||
Are you sure you want remove hotbar
|
||||
{" "}
|
||||
<b>
|
||||
{option.value.name}
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
} }
|
||||
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
||||
menuIsOpen={true}
|
||||
options={(
|
||||
hotbarStore.hotbars
|
||||
.map(hotbar => ({
|
||||
value: hotbar,
|
||||
label: hotbarStore.getDisplayLabel(hotbar),
|
||||
}))
|
||||
)}
|
||||
autoFocus={true}
|
||||
escapeClearsValue={false}
|
||||
placeholder="Remove hotbar"
|
||||
/>
|
||||
));
|
||||
|
||||
export const HotbarRemoveCommand = withInjectables<Dependencies>(NonInjectedHotbarRemoveCommand, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
closeCommandOverlay: di.inject(commandOverlayInjectable).close,
|
||||
hotbarStore: di.inject(hotbarStoreInjectable),
|
||||
openConfirmDialog: di.inject(openConfirmDialogInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
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";
|
||||
|
||||
interface Dependencies {
|
||||
closeCommandOverlay: () => void;
|
||||
hotbarStore: HotbarStore;
|
||||
uniqueHotbarName: InputValidator<false>;
|
||||
}
|
||||
|
||||
const NonInjectedHotbarRenameCommand = observer(({
|
||||
closeCommandOverlay,
|
||||
hotbarStore,
|
||||
uniqueHotbarName,
|
||||
}: Dependencies) => {
|
||||
const [hotbarId, setHotbarId] = useState("");
|
||||
const [hotbarName, setHotbarName] = useState("");
|
||||
|
||||
const onSubmit = (name: string) => {
|
||||
if (!name.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
hotbarStore.setHotbarName(hotbarId, name);
|
||||
closeCommandOverlay();
|
||||
};
|
||||
|
||||
if (hotbarId) {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
trim={true}
|
||||
value={hotbarName}
|
||||
onChange={setHotbarName}
|
||||
placeholder="New hotbar name"
|
||||
autoFocus={true}
|
||||
theme="round-black"
|
||||
validators={uniqueHotbarName}
|
||||
onSubmit={onSubmit}
|
||||
showValidationLine={true}
|
||||
/>
|
||||
<small className="hint">
|
||||
Please provide a new hotbar name (Press "Enter" to confirm or "Escape" to cancel)
|
||||
</small>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Select
|
||||
id="rename-hotbar-input"
|
||||
menuPortalTarget={null}
|
||||
onChange={(option) => {
|
||||
if (option) {
|
||||
setHotbarId(option.value.id);
|
||||
setHotbarName(option.value.name);
|
||||
}
|
||||
}}
|
||||
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
||||
menuIsOpen={true}
|
||||
options={(
|
||||
hotbarStore.hotbars
|
||||
.map(hotbar => ({
|
||||
value: hotbar,
|
||||
label: hotbarStore.getDisplayLabel(hotbar),
|
||||
}))
|
||||
)}
|
||||
autoFocus={true}
|
||||
escapeClearsValue={false}
|
||||
placeholder="Rename hotbar"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const HotbarRenameCommand = withInjectables<Dependencies>(NonInjectedHotbarRenameCommand, {
|
||||
getProps: (di, props) => ({
|
||||
closeCommandOverlay: di.inject(commandOverlayInjectable).close,
|
||||
hotbarStore: di.inject(hotbarStoreInjectable),
|
||||
uniqueHotbarName: di.inject(uniqueHotbarNameInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.HotbarSelector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 25px;
|
||||
background-color: var(--layoutBackground);
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background: linear-gradient(0deg, var(--clusterMenuBackground), transparent);
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
.HotbarIndex {
|
||||
display: flex;
|
||||
width: 3ch;
|
||||
}
|
||||
|
||||
.Badge {
|
||||
cursor: pointer;
|
||||
background: var(--secondaryBackground);
|
||||
width: 100%;
|
||||
color: var(--settingsColor);
|
||||
padding: 0px calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
.Icon {
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
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 "../../utils";
|
||||
import type { HotbarStore } from "../../../common/hotbars/store";
|
||||
|
||||
interface Dependencies {
|
||||
hotbarStore: HotbarStore;
|
||||
openCommandOverlay: (component: React.ReactElement) => void;
|
||||
}
|
||||
|
||||
export interface HotbarSelectorProps extends Partial<Dependencies> {
|
||||
hotbar: Hotbar;
|
||||
}
|
||||
|
||||
const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOverlay }: HotbarSelectorProps & Dependencies) => {
|
||||
const [tooltipVisible, setTooltipVisible] = useState(false);
|
||||
const tooltipTimeout = useRef<number>();
|
||||
|
||||
function clearTimer() {
|
||||
clearTimeout(tooltipTimeout.current);
|
||||
}
|
||||
|
||||
function onTooltipShow() {
|
||||
setTooltipVisible(true);
|
||||
clearTimer();
|
||||
tooltipTimeout.current = window.setTimeout(() => setTooltipVisible(false), 1500);
|
||||
}
|
||||
|
||||
function onPrevClick() {
|
||||
onTooltipShow();
|
||||
hotbarStore.switchToPrevious();
|
||||
}
|
||||
|
||||
function onNextClick() {
|
||||
onTooltipShow();
|
||||
hotbarStore.switchToNext();
|
||||
}
|
||||
|
||||
function onMouseEvent(event: React.MouseEvent) {
|
||||
clearTimer();
|
||||
setTooltipVisible(event.type == "mouseenter");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.HotbarSelector}>
|
||||
<Icon
|
||||
material="arrow_left"
|
||||
className={cssNames(styles.Icon, styles.previous)}
|
||||
onClick={onPrevClick}/>
|
||||
<div className={styles.HotbarIndex}>
|
||||
<Badge
|
||||
id="hotbarIndex"
|
||||
small
|
||||
label={hotbarStore.getDisplayIndex(hotbarStore.getActive())}
|
||||
onClick={() => openCommandOverlay(<HotbarSwitchCommand />)}
|
||||
className={styles.Badge}
|
||||
onMouseEnter={onMouseEvent}
|
||||
onMouseLeave={onMouseEvent}
|
||||
/>
|
||||
<Tooltip
|
||||
visible={tooltipVisible}
|
||||
targetId="hotbarIndex"
|
||||
preferredPositions={[TooltipPosition.TOP, TooltipPosition.TOP_LEFT]}
|
||||
>
|
||||
{hotbar.name}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Icon
|
||||
material="arrow_right"
|
||||
className={styles.Icon}
|
||||
onClick={onNextClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const HotbarSelector = withInjectables<Dependencies, HotbarSelectorProps>(NonInjectedHotbarSelector, {
|
||||
getProps: (di, props) => ({
|
||||
hotbarStore: di.inject(hotbarStoreInjectable),
|
||||
openCommandOverlay: di.inject(commandOverlayInjectable).open,
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
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";
|
||||
|
||||
const hotbarAddAction = Symbol("hotbar-add");
|
||||
const hotbarRemoveAction = Symbol("hotbar-remove");
|
||||
const hotbarRenameAction = Symbol("hotbar-rename");
|
||||
|
||||
interface Dependencies {
|
||||
hotbarStore: HotbarStore;
|
||||
commandOverlay: CommandOverlay;
|
||||
}
|
||||
|
||||
function ignoreIf<T>(check: boolean, menuItems: T[]): T[] {
|
||||
return check ? [] : menuItems;
|
||||
}
|
||||
|
||||
const NonInjectedHotbarSwitchCommand = observer(({
|
||||
hotbarStore,
|
||||
commandOverlay,
|
||||
}: Dependencies) => (
|
||||
<Select
|
||||
id="switch-to-hotbar-input"
|
||||
menuPortalTarget={null}
|
||||
onChange={(option) => {
|
||||
if (!option) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof option.value === "symbol") {
|
||||
switch (option.value) {
|
||||
case hotbarAddAction:
|
||||
return commandOverlay.open(<HotbarAddCommand />);
|
||||
case hotbarRemoveAction:
|
||||
return commandOverlay.open(<HotbarRemoveCommand />);
|
||||
case hotbarRenameAction:
|
||||
return commandOverlay.open(<HotbarRenameCommand />);
|
||||
}
|
||||
} else {
|
||||
hotbarStore.setActiveHotbar(option.value);
|
||||
commandOverlay.close();
|
||||
}
|
||||
}}
|
||||
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
||||
menuIsOpen={true}
|
||||
options={[
|
||||
...hotbarStore.hotbars.map(hotbar => ({
|
||||
value: hotbar,
|
||||
label: hotbarStore.getDisplayLabel(hotbar),
|
||||
})),
|
||||
{
|
||||
value: hotbarAddAction,
|
||||
label: "Add hotbar ...",
|
||||
},
|
||||
...ignoreIf(hotbarStore.hotbars.length > 1, [
|
||||
{
|
||||
value: hotbarRemoveAction,
|
||||
label: "Remove hotbar ...",
|
||||
},
|
||||
]),
|
||||
{
|
||||
value: hotbarRenameAction,
|
||||
label: "Rename hotbar ...",
|
||||
},
|
||||
]}
|
||||
autoFocus={true}
|
||||
escapeClearsValue={false}
|
||||
isClearable={false}
|
||||
placeholder="Switch to hotbar"
|
||||
/>
|
||||
));
|
||||
|
||||
export const HotbarSwitchCommand = withInjectables<Dependencies>(NonInjectedHotbarSwitchCommand, {
|
||||
getProps: (di, props) => ({
|
||||
hotbarStore: di.inject(hotbarStoreInjectable),
|
||||
commandOverlay: di.inject(commandOverlayInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
Reference in new issue
Block a user