1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

show active item in hotbar & allow to pin it

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen 2021-05-18 09:12:44 +03:00
parent 6a33944f52
commit 0a1346fa59
6 changed files with 96 additions and 86 deletions

View File

@ -21,6 +21,7 @@
import mockFs from "mock-fs";
import { CatalogEntityItem } from "../../renderer/components/+catalog/catalog-entity.store";
import { CatalogEntity } from "../catalog";
import { ClusterStore } from "../cluster-store";
import { HotbarStore } from "../hotbar-store";
@ -159,10 +160,9 @@ describe("HotbarStore", () => {
it("adds items", () => {
const hotbarStore = HotbarStore.createInstance();
const entity = new CatalogEntityItem(testCluster);
hotbarStore.load();
hotbarStore.addToHotbar(entity);
hotbarStore.addToHotbar(testCluster);
const items = hotbarStore.getActive().items.filter(Boolean);
expect(items.length).toEqual(1);
@ -170,10 +170,9 @@ describe("HotbarStore", () => {
it("removes items", () => {
const hotbarStore = HotbarStore.createInstance();
const entity = new CatalogEntityItem(testCluster);
hotbarStore.load();
hotbarStore.addToHotbar(entity);
hotbarStore.addToHotbar(testCluster);
hotbarStore.removeFromHotbar("test");
const items = hotbarStore.getActive().items.filter(Boolean);
@ -182,10 +181,9 @@ describe("HotbarStore", () => {
it("does nothing if removing with invalid uid", () => {
const hotbarStore = HotbarStore.createInstance();
const entity = new CatalogEntityItem(testCluster);
hotbarStore.load();
hotbarStore.addToHotbar(entity);
hotbarStore.addToHotbar(testCluster);
hotbarStore.removeFromHotbar("invalid uid");
const items = hotbarStore.getActive().items.filter(Boolean);
@ -194,14 +192,11 @@ describe("HotbarStore", () => {
it("moves item to empty cell", () => {
const hotbarStore = HotbarStore.createInstance();
const test = new CatalogEntityItem(testCluster);
const minikube = new CatalogEntityItem(minikubeCluster);
const aws = new CatalogEntityItem(awsCluster);
hotbarStore.load();
hotbarStore.addToHotbar(test);
hotbarStore.addToHotbar(minikube);
hotbarStore.addToHotbar(aws);
hotbarStore.addToHotbar(testCluster);
hotbarStore.addToHotbar(minikubeCluster);
hotbarStore.addToHotbar(awsCluster);
expect(hotbarStore.getActive().items[5]).toBeNull();
@ -213,14 +208,11 @@ describe("HotbarStore", () => {
it("moves items down", () => {
const hotbarStore = HotbarStore.createInstance();
const test = new CatalogEntityItem(testCluster);
const minikube = new CatalogEntityItem(minikubeCluster);
const aws = new CatalogEntityItem(awsCluster);
hotbarStore.load();
hotbarStore.addToHotbar(test);
hotbarStore.addToHotbar(minikube);
hotbarStore.addToHotbar(aws);
hotbarStore.addToHotbar(testCluster);
hotbarStore.addToHotbar(minikubeCluster);
hotbarStore.addToHotbar(awsCluster);
// aws -> test
hotbarStore.restackItems(2, 0);
@ -232,14 +224,11 @@ describe("HotbarStore", () => {
it("moves items up", () => {
const hotbarStore = HotbarStore.createInstance();
const test = new CatalogEntityItem(testCluster);
const minikube = new CatalogEntityItem(minikubeCluster);
const aws = new CatalogEntityItem(awsCluster);
hotbarStore.load();
hotbarStore.addToHotbar(test);
hotbarStore.addToHotbar(minikube);
hotbarStore.addToHotbar(aws);
hotbarStore.addToHotbar(testCluster);
hotbarStore.addToHotbar(minikubeCluster);
hotbarStore.addToHotbar(awsCluster);
// test -> aws
hotbarStore.restackItems(0, 2);
@ -251,10 +240,9 @@ describe("HotbarStore", () => {
it("does nothing when item moved to same cell", () => {
const hotbarStore = HotbarStore.createInstance();
const test = new CatalogEntityItem(testCluster);
hotbarStore.load();
hotbarStore.addToHotbar(test);
hotbarStore.addToHotbar(testCluster);
hotbarStore.restackItems(0, 0);
expect(hotbarStore.getActive().items[0].entity.uid).toEqual("test");
@ -262,15 +250,12 @@ describe("HotbarStore", () => {
it("new items takes first empty cell", () => {
const hotbarStore = HotbarStore.createInstance();
const test = new CatalogEntityItem(testCluster);
const minikube = new CatalogEntityItem(minikubeCluster);
const aws = new CatalogEntityItem(awsCluster);
hotbarStore.load();
hotbarStore.addToHotbar(test);
hotbarStore.addToHotbar(aws);
hotbarStore.addToHotbar(testCluster);
hotbarStore.addToHotbar(awsCluster);
hotbarStore.restackItems(0, 3);
hotbarStore.addToHotbar(minikube);
hotbarStore.addToHotbar(minikubeCluster);
expect(hotbarStore.getActive().items[0].entity.uid).toEqual("minikube");
});
@ -282,10 +267,9 @@ describe("HotbarStore", () => {
console.error = jest.fn();
const hotbarStore = HotbarStore.createInstance();
const test = new CatalogEntityItem(testCluster);
hotbarStore.load();
hotbarStore.addToHotbar(test);
hotbarStore.addToHotbar(testCluster);
expect(() => hotbarStore.restackItems(-5, 0)).toThrow();
expect(() => hotbarStore.restackItems(2, -1)).toThrow();

View File

@ -23,8 +23,8 @@ import { action, comparer, observable, toJS } from "mobx";
import { BaseStore } from "./base-store";
import migrations from "../migrations/hotbar-store";
import * as uuid from "uuid";
import { CatalogEntityItem } from "../renderer/components/+catalog/catalog-entity.store";
import isNull from "lodash/isNull";
import { CatalogEntity } from "./catalog";
export interface HotbarItem {
entity: {
@ -151,15 +151,15 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
}
@action
addToHotbar(item: CatalogEntityItem, cellIndex = -1) {
addToHotbar(item: CatalogEntity, cellIndex = -1) {
const hotbar = this.getActive();
const newItem = { entity: {
uid: item.id,
name: item.name,
source: item.source
uid: item.metadata.uid,
name: item.metadata.name,
source: item.metadata.source
}};
if (hotbar.items.find(i => i?.entity.uid === item.id)) {
if (hotbar.items.find(i => i?.entity.uid === item.metadata.uid)) {
return;
}

View File

@ -68,7 +68,7 @@ export class Catalog extends React.Component {
}
addToHotbar(item: CatalogEntityItem): void {
HotbarStore.getInstance().addToHotbar(item);
HotbarStore.getInstance().addToHotbar(item.entity);
}
onDetails(item: CatalogEntityItem) {
@ -137,7 +137,7 @@ export class Catalog extends React.Component {
return (
<MenuActions onOpen={() => item.onContextMenuOpen(this.contextMenu)}>
<MenuItem key="add-to-hotbar" onClick={() => this.addToHotbar(item) }>
<Icon material="add" small interactive={true} title="Add to hotbar"/> Add to Hotbar
<Icon material="push_pin" small interactive={true} title="Pin to Hotbar"/> Pin to Hotbar
</MenuItem>
{
menuItems.map((menuItem, index) => (

View File

@ -23,21 +23,22 @@ import "./hotbar-icon.scss";
import React, { DOMAttributes } from "react";
import { observable } from "mobx";
import { observer } from "mobx-react";
import randomColor from "randomcolor";
import { CatalogEntity, CatalogEntityContextMenu, CatalogEntityContextMenuContext } from "../../../common/catalog";
import { CatalogEntity, CatalogEntityContextMenuContext } from "../../../common/catalog";
import { catalogCategoryRegistry } from "../../api/catalog-category-registry";
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
import { navigate } from "../../navigation";
import { cssNames, IClassName } from "../../utils";
import { ConfirmDialog } from "../confirm-dialog";
import { Icon } from "../icon";
import { HotbarIcon } from "./hotbar-icon";
import { HotbarStore } from "../../../common/hotbar-store";
interface Props extends DOMAttributes<HTMLElement> {
entity: CatalogEntity;
index: number;
className?: IClassName;
errorClass?: IClassName;
add: (item: CatalogEntity, index: number) => void;
remove: (uid: string) => void;
}
@ -77,33 +78,18 @@ export class HotbarEntityIcon extends React.Component<Props> {
return catalogEntityRegistry.activeEntity?.metadata?.uid == item.getId();
}
onMenuItemClick(menuItem: CatalogEntityContextMenu) {
if (menuItem.confirm) {
ConfirmDialog.open({
okButtonProps: {
primary: false,
accent: true,
},
ok: () => {
menuItem.onClick();
},
message: menuItem.confirm.message
});
} else {
menuItem.onClick();
}
}
generateAvatarStyle(entity: CatalogEntity): React.CSSProperties {
return {
"backgroundColor": randomColor({ seed: `${entity.metadata.name}-${entity.metadata.source}`, luminosity: "dark" })
};
isPersisted(entity: CatalogEntity) {
return HotbarStore.getInstance().getActive().items.find((item) => item?.entity?.uid === entity.metadata.uid) !== undefined;
}
render() {
if (!this.contextMenu) {
return null;
}
const {
entity, errorClass, remove,
children, ...elemProps
entity, errorClass, add, remove,
index, children, ...elemProps
} = this.props;
const className = cssNames("HotbarEntityIcon", this.props.className, {
interactive: true,
@ -113,16 +99,33 @@ export class HotbarEntityIcon extends React.Component<Props> {
const onOpen = async () => {
await entity.onContextMenuOpen(this.contextMenu);
};
const isActive = this.isActive(entity);
const isPersisted = this.isPersisted(entity);
const menuItems = this.contextMenu?.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === entity.metadata.source);
console.log(entity.metadata.name, isActive);
if (!isPersisted) {
menuItems.unshift({
title: "Pin to Hotbar",
icon: "push_pin",
onClick: () => add(entity, index)
});
} else {
menuItems.unshift({
title: "Unpin from Hotbar",
icon: "push_pin",
onClick: () => remove(entity.metadata.uid)
});
}
return (
<HotbarIcon
uid={entity.getId()}
title={entity.getName()}
source={`${entity.metadata.source || "local"}`}
uid={entity.metadata.uid}
title={entity.metadata.name}
source={entity.metadata.source}
className={className}
active={this.isActive(entity)}
remove={remove}
active={isActive}
onMenuOpen={onOpen}
menuItems={menuItems}
{...elemProps}

View File

@ -37,7 +37,6 @@ interface Props extends DOMAttributes<HTMLElement> {
uid: string;
title: string;
source: string;
remove: (uid: string) => void;
onMenuOpen?: () => void;
className?: IClassName;
active?: boolean;
@ -85,7 +84,7 @@ function getNameParts(name: string): string[] {
}
export function HotbarIcon(props: Props) {
const { uid, title, className, source, active, remove, disabled, menuItems, onMenuOpen, children, ...rest } = props;
const { uid, title, className, source, active, disabled, menuItems, onMenuOpen, children, ...rest } = props;
const id = `hotbarIcon-${uid}`;
const [menuOpen, setMenuOpen] = useState(false);
@ -114,7 +113,7 @@ export function HotbarIcon(props: Props) {
<Avatar
{...rest}
variant="square"
className={active ? "active" : "default"}
className="active"
style={generateAvatarStyle(`${title}-${source}`)}
>
{getIconString()}
@ -134,12 +133,6 @@ export function HotbarIcon(props: Props) {
toggleMenu();
}}
close={() => toggleMenu()}>
<MenuItem key="remove-from-hotbar" onClick={(evt) => {
evt.stopPropagation();
remove(uid);
}}>
<Icon material="clear" small interactive={true} title="Remove from hotbar"/> Remove from Hotbar
</MenuItem>
{ menuItems.map((menuItem) => {
return (
<MenuItem key={menuItem.title} onClick={() => onMenuItemClick(menuItem) }>

View File

@ -28,11 +28,12 @@ import { HotbarEntityIcon } from "./hotbar-entity-icon";
import { cssNames, IClassName } from "../../utils";
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
import { defaultHotbarCells, HotbarItem, HotbarStore } from "../../../common/hotbar-store";
import { catalogEntityRunContext } from "../../api/catalog-entity";
import { CatalogEntity, catalogEntityRunContext } from "../../api/catalog-entity";
import { DragDropContext, Draggable, Droppable, DropResult } from "react-beautiful-dnd";
import { HotbarSelector } from "./hotbar-selector";
import { HotbarCell } from "./hotbar-cell";
import { HotbarIcon } from "./hotbar-icon";
import { computed } from "mobx";
interface Props {
className?: IClassName;
@ -64,6 +65,10 @@ export class HotbarMenu extends React.Component<Props> {
const from = parseInt(source.droppableId);
const to = parseInt(destination.droppableId);
if (!this.hotbar.items[from]) { // Dropped non-persisted item
this.hotbar.items[from] = this.items[from];
}
HotbarStore.getInstance().restackItems(from, to);
}
@ -73,14 +78,38 @@ export class HotbarMenu extends React.Component<Props> {
hotbar.removeFromHotbar(uid);
}
addItem(entity: CatalogEntity, index = -1) {
const hotbar = HotbarStore.getInstance();
hotbar.addToHotbar(entity, index);
}
getMoveAwayDirection(entityId: string, cellIndex: number) {
const draggableItemIndex = this.hotbar.items.findIndex(item => item?.entity.uid == entityId);
return draggableItemIndex > cellIndex ? "animateDown" : "animateUp";
}
@computed get items() {
const items = this.hotbar.items;
const activeEntity = catalogEntityRegistry.activeEntity;
if (!activeEntity) return items;
const emptyIndex = items.indexOf(null);
if (emptyIndex === -1) return items;
if (items.find((item) => item?.entity?.uid === activeEntity.metadata.uid)) return items;
const modifiedItems = [...items];
modifiedItems.splice(emptyIndex, 1, { entity: { uid: activeEntity.metadata.uid }});
return modifiedItems;
}
renderGrid() {
return this.hotbar.items.map((item, index) => {
return this.items.map((item, index) => {
const entity = this.getEntity(item);
return (
@ -116,17 +145,18 @@ export class HotbarMenu extends React.Component<Props> {
{entity ? (
<HotbarEntityIcon
key={index}
index={index}
entity={entity}
onClick={() => entity.onRun(catalogEntityRunContext)}
className={cssNames({ isDragging: snapshot.isDragging })}
remove={this.removeItem}
add={this.addItem}
/>
) : (
<HotbarIcon
uid={item.entity.uid}
title={item.entity.name}
source={item.entity.source}
remove={this.removeItem}
disabled
/>
)}
@ -151,7 +181,7 @@ export class HotbarMenu extends React.Component<Props> {
return (
<div className={cssNames("HotbarMenu flex column", className)}>
<div className="HotbarItems flex column gaps">
<DragDropContext onDragEnd={this.onDragEnd}>
<DragDropContext onDragEnd={this.onDragEnd.bind(this)}>
{this.renderGrid()}
</DragDropContext>
</div>