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

show catalog categories

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen 2021-04-08 15:13:23 +03:00
parent a946d7989a
commit f42cac6b3b
8 changed files with 142 additions and 13 deletions

View File

@ -1 +1,2 @@
export * from "./kubernetes-cluster";
export * from "./web-link";

View File

@ -96,6 +96,10 @@ export class KubernetesClusterCategory extends EventEmitter implements CatalogCa
kind: "KubernetesCluster"
}
};
getId() {
return `${this.spec.group}/${this.spec.names.kind}`;
}
}
catalogCategoryRegistry.add(new KubernetesClusterCategory());

View File

@ -0,0 +1,65 @@
import { observable } from "mobx";
import { CatalogCategory, CatalogEntity, CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog-entity";
import { catalogCategoryRegistry } from "../catalog-category-registry";
export interface WebLinkStatus extends CatalogEntityStatus {
phase: "valid" | "invalid";
}
export type WebLinkSpec = {
url: string;
};
export class WebLink implements CatalogEntity {
public readonly apiVersion = "entity.k8slens.dev/v1alpha1";
public readonly kind = "KubernetesCluster";
@observable public metadata: CatalogEntityMetadata;
@observable public status: WebLinkStatus;
@observable public spec: WebLinkSpec;
getId() {
return this.metadata.uid;
}
getName() {
return this.metadata.name;
}
async onRun(context: CatalogEntityActionContext) {
window.open(this.spec.url, "_blank");
}
async onDetailsOpen() {
//
}
async onContextMenuOpen(context: CatalogEntityContextMenuContext) {
//
}
}
export class WebLinkCategory implements CatalogCategory {
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
public readonly kind = "CatalogCategory";
public metadata = {
name: "Web Link"
};
public spec = {
group: "entity.k8slens.dev",
versions: [
{
name: "v1alpha1",
entityClass: WebLink
}
],
names: {
kind: "WebLink"
}
};
getId() {
return `${this.spec.group}/${this.spec.names.kind}`;
}
}
catalogCategoryRegistry.add(new WebLinkCategory());

View File

@ -16,6 +16,7 @@ export interface CatalogCategory {
kind: string;
}
}
getId: () => string;
}
export type CatalogEntityMetadata = {

View File

@ -1,6 +1,6 @@
import { action, observable } from "mobx";
import { broadcastMessage, subscribeToBroadcast } from "../../common/ipc";
import { CatalogEntity, CatalogEntityData } from "../../common/catalog-entity";
import { CatalogCategory, CatalogEntity, CatalogEntityData } from "../../common/catalog-entity";
import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../common/catalog-category-registry";
import "../../common/catalog-entities";
@ -49,6 +49,13 @@ export class CatalogEntityRegistry {
return items as T[];
}
getItemsForCategory<T extends CatalogEntity>(category: CatalogCategory): T[] {
const supportedVersions = category.spec.versions.map((v) => `${category.spec.group}/${v.name}`);
const items = this._items.filter((item) => supportedVersions.includes(item.apiVersion) && item.kind === category.spec.names.kind);
return items as T[];
}
}
export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);

View File

@ -1,8 +1,9 @@
import { action, computed, reaction } from "mobx";
import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
import { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
import { ItemObject, ItemStore } from "../../item.store";
import { autobind } from "../../utils";
import { CatalogCategory } from "../../../common/catalog-entity";
export class CatalogEntityItem implements ItemObject {
constructor(public entity: CatalogEntity) {}
@ -55,13 +56,23 @@ export class CatalogEntityItem implements ItemObject {
@autobind()
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
@observable activeCategory: CatalogCategory;
@computed get entities() {
return catalogEntityRegistry.items.map(entity => new CatalogEntityItem(entity));
if (!this.activeCategory) return [];
console.log("computing entities", this.activeCategory);
return catalogEntityRegistry.getItemsForCategory(this.activeCategory).map(entity => new CatalogEntityItem(entity));
}
watch() {
return reaction(() => this.entities, () => this.loadAll());
const disposers: IReactionDisposer[] = [
reaction(() => this.entities, () => this.loadAll()),
reaction(() => this.activeCategory, () => this.loadAll(), { delay: 100})
];
return () => disposers.forEach((dispose) => dispose());
}
loadAll() {

View File

@ -1,13 +1,16 @@
.CatalogPage {
--width: 100%;
--height: 100%;
--nav-column-width: 230px;
text-align: left;
.content-wrapper {
.sidebarRegion {
justify-content: flex-start;
}
.contentRegion {
.content {
margin: unset;
max-width: unset;
height: 100%;
padding: 20px 20px;
}
}

View File

@ -2,7 +2,7 @@ import "./catalog.scss";
import React from "react";
import { disposeOnUnmount, observer } from "mobx-react";
import { ItemListLayout } from "../item-object-list";
import { observable } from "mobx";
import { observable, reaction } from "mobx";
import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
import { navigate } from "../../navigation";
import { kebabCase } from "lodash";
@ -16,17 +16,19 @@ import { addClusterURL } from "../+add-cluster";
import { autobind } from "../../utils";
import { Notifications } from "../notifications";
import { ConfirmDialog } from "../confirm-dialog";
import { Tab, Tabs } from "../tabs";
import { catalogCategoryRegistry } from "../../../common/catalog-category-registry";
enum sortBy {
name = "name",
source = "source",
status = "status"
}
@observer
export class Catalog extends React.Component {
@observable private catalogEntityStore?: CatalogEntityStore;
@observable.deep private contextMenu: CatalogEntityContextMenuContext;
@observable activeTab: string;
async componentDidMount() {
this.contextMenu = {
@ -35,7 +37,13 @@ export class Catalog extends React.Component {
};
this.catalogEntityStore = new CatalogEntityStore();
disposeOnUnmount(this, [
this.catalogEntityStore.watch()
this.catalogEntityStore.watch(),
reaction(() => catalogCategoryRegistry.items, (items) => {
if (!this.activeTab && items.length > 0) {
this.activeTab = items[0].getId();
this.catalogEntityStore.activeCategory = items[0];
}
}, { fireImmediately: true })
]);
setTimeout(() => {
@ -89,6 +97,31 @@ export class Catalog extends React.Component {
}
}
get categories() {
return catalogCategoryRegistry.items;
}
onTabChange = (tabId: string) => {
this.activeTab = tabId;
const activeCategory = this.categories.find((category) => category.getId() === tabId);
if (activeCategory) {
this.catalogEntityStore.activeCategory = activeCategory;
}
};
renderNavigation() {
return (
<Tabs className="flex column" scrollable={false} onChange={this.onTabChange} value={this.activeTab}>
<div className="header">Catalog</div>
{ this.categories.map((category, index) => {
return <Tab value={category.getId()} key={index} label={category.metadata.name} data-testid={`${category.getId()}-tab`} />;
})}
</Tabs>
);
}
@autobind()
renderItemMenu(item: CatalogEntityItem) {
const onOpen = async () => {
@ -120,14 +153,18 @@ export class Catalog extends React.Component {
}
return (
<PageLayout className="CatalogPage" provideBackButtonNavigation={false}>
<PageLayout
className="CatalogPage"
navigation={this.renderNavigation()}
provideBackButtonNavigation={false}
contentGaps={false}>
<ItemListLayout
renderHeaderTitle="Catalog"
isClusterScoped
isSearchable={true}
isSelectable={false}
className="CatalogItemList"
store={this.catalogEntityStore}
tableId="catalog-items"
sortingCallbacks={{
[sortBy.name]: (item: CatalogEntityItem) => item.name,
[sortBy.source]: (item: CatalogEntityItem) => item.source,