mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Catalog & Hotbar - initial groundwork (#2418)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
e18a041b13
commit
99a464c61d
109 files changed
+1604
-2612
No files matched your search
@@ -0,0 +1,58 @@
|
||||
.HotbarMenu {
|
||||
.HotbarIcon {
|
||||
--size: 37px;
|
||||
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
padding: 2px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
div.MuiAvatar-colorDefault {
|
||||
font-weight:500;
|
||||
text-transform: uppercase;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
div.active {
|
||||
background-color: var(--primary);
|
||||
}
|
||||
|
||||
div.default {
|
||||
background-color: var(--halfGray);
|
||||
}
|
||||
|
||||
&.active {
|
||||
margin-left: -3px;
|
||||
border: 3px solid #fff;
|
||||
}
|
||||
|
||||
&.active, &.interactive:hover {
|
||||
|
||||
div {
|
||||
background-color: var(--primary);
|
||||
}
|
||||
|
||||
img {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
}
|
||||
}
|
||||
|
||||
.HotbarIconMenu {
|
||||
left: 30px;
|
||||
min-width: 250px;
|
||||
|
||||
ul {
|
||||
li {
|
||||
font-size: 12px;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import "./hotbar-icon.scss";
|
||||
|
||||
import React, { DOMAttributes } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { cssNames, IClassName } from "../../utils";
|
||||
import { Tooltip } from "../tooltip";
|
||||
import { Avatar } from "@material-ui/core";
|
||||
import { CatalogEntity, CatalogEntityContextMenu, CatalogEntityContextMenuContext } from "../../../common/catalog-entity";
|
||||
import { Menu, MenuItem } from "../menu";
|
||||
import { Icon } from "../icon";
|
||||
import { observable } from "mobx";
|
||||
import { navigate } from "../../navigation";
|
||||
import { hotbarStore } from "../../../common/hotbar-store";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
|
||||
interface Props extends DOMAttributes<HTMLElement> {
|
||||
entity: CatalogEntity;
|
||||
index: number;
|
||||
className?: IClassName;
|
||||
errorClass?: IClassName;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class HotbarIcon extends React.Component<Props> {
|
||||
@observable.deep private contextMenu: CatalogEntityContextMenuContext;
|
||||
@observable menuOpen = false;
|
||||
|
||||
componentDidMount() {
|
||||
this.contextMenu = {
|
||||
menuItems: [],
|
||||
navigate: (url: string) => navigate(url)
|
||||
};
|
||||
}
|
||||
|
||||
get iconString() {
|
||||
let splittedName = this.props.entity.metadata.name.split(" ");
|
||||
|
||||
if (splittedName.length === 1) {
|
||||
splittedName = splittedName[0].split("-");
|
||||
}
|
||||
|
||||
if (splittedName.length === 1) {
|
||||
splittedName = splittedName[0].split("@");
|
||||
}
|
||||
|
||||
splittedName = splittedName.map((part) => part.replace(/\W/g, ""));
|
||||
|
||||
if (splittedName.length === 1) {
|
||||
return splittedName[0].substring(0, 2);
|
||||
} else if (splittedName.length === 2) {
|
||||
return splittedName[0].substring(0, 1) + splittedName[1].substring(0, 1);
|
||||
} else {
|
||||
return splittedName[0].substring(0, 1) + splittedName[1].substring(0, 1) + splittedName[2].substring(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
toggleMenu() {
|
||||
this.menuOpen = !this.menuOpen;
|
||||
}
|
||||
|
||||
removeFromHotbar(item: CatalogEntity) {
|
||||
const hotbar = hotbarStore.getByName("default"); // FIXME
|
||||
|
||||
if (!hotbar) {
|
||||
return;
|
||||
}
|
||||
|
||||
hotbar.items = hotbar.items.filter((i) => i.entity.uid !== item.metadata.uid);
|
||||
}
|
||||
|
||||
onMenuItemClick(menuItem: CatalogEntityContextMenu) {
|
||||
if (menuItem.confirm) {
|
||||
ConfirmDialog.open({
|
||||
okButtonProps: {
|
||||
primary: false,
|
||||
accent: true,
|
||||
},
|
||||
ok: () => {
|
||||
menuItem.onClick();
|
||||
},
|
||||
message: menuItem.confirm.message
|
||||
});
|
||||
} else {
|
||||
menuItem.onClick();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
entity, errorClass, isActive,
|
||||
children, ...elemProps
|
||||
} = this.props;
|
||||
const entityIconId = `hotbar-icon-${this.props.index}`;
|
||||
const className = cssNames("HotbarIcon flex inline", this.props.className, {
|
||||
interactive: true,
|
||||
active: isActive,
|
||||
});
|
||||
const onOpen = async () => {
|
||||
await entity.onContextMenuOpen(this.contextMenu);
|
||||
this.toggleMenu();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Tooltip targetId={entityIconId}>{entity.metadata.name}</Tooltip>
|
||||
<Avatar {...elemProps} id={entityIconId} variant="square" className={isActive ? "active" : "default"}>{this.iconString}</Avatar>
|
||||
<Menu
|
||||
usePortal={false}
|
||||
htmlFor={entityIconId}
|
||||
className="HotbarIconMenu"
|
||||
isOpen={this.menuOpen}
|
||||
toggleEvent="contextmenu"
|
||||
position={{right: true, bottom: true }} // FIXME: position does not work
|
||||
open={() => onOpen()}
|
||||
close={() => this.toggleMenu()}>
|
||||
<MenuItem key="remove-from-hotbar" onClick={() => this.removeFromHotbar(entity) }>
|
||||
<Icon material="clear" small interactive={true} title="Remove from hotbar"/> Remove from Hotbar
|
||||
</MenuItem>
|
||||
{ this.contextMenu && this.contextMenu.menuItems.map((menuItem) => {
|
||||
return (
|
||||
<MenuItem key={menuItem.title} onClick={() => this.onMenuItemClick(menuItem) }>
|
||||
<Icon material={menuItem.icon} small interactive={true} title={menuItem.title}/> {menuItem.title}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
.HotbarMenu {
|
||||
$spacing: $padding * 2;
|
||||
|
||||
position: relative;
|
||||
text-align: center;
|
||||
background: $clusterMenuBackground;
|
||||
border-right: 1px solid $clusterMenuBorderColor;
|
||||
padding: $spacing 0;
|
||||
min-width: 75px;
|
||||
|
||||
.is-mac &:before {
|
||||
content: "";
|
||||
height: 20px; // extra spacing for mac-os "traffic-light" buttons
|
||||
}
|
||||
|
||||
.items {
|
||||
padding: 0 $spacing; // extra spacing for cluster-icon's badge
|
||||
margin-bottom: $margin;
|
||||
overflow: visible;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import "./hotbar-menu.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { HotbarIcon } from "./hotbar-icon";
|
||||
import { cssNames, IClassName } from "../../utils";
|
||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import { hotbarStore } from "../../../common/hotbar-store";
|
||||
import { catalogEntityRunContext } from "../../api/catalog-entity";
|
||||
|
||||
interface Props {
|
||||
className?: IClassName;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class HotbarMenu extends React.Component<Props> {
|
||||
render() {
|
||||
const { className } = this.props;
|
||||
const hotbar = hotbarStore.getByName("default"); // FIXME
|
||||
|
||||
if (!hotbar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const items = hotbar.items.map((item) => catalogEntityRegistry.items.find((entity) => entity.metadata.uid === item.entity.uid)).filter(Boolean);
|
||||
|
||||
return (
|
||||
<div className={cssNames("HotbarMenu flex column", className)}>
|
||||
<div className="items flex column gaps">
|
||||
{items.map((entity, index) => {
|
||||
return (
|
||||
<HotbarIcon
|
||||
key={index}
|
||||
index={index}
|
||||
entity={entity}
|
||||
isActive={entity.status.active}
|
||||
onClick={() => entity.onRun(catalogEntityRunContext)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user