From f42cac6b3b636bb2cce5f0cc7143569ceb03ed04 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Thu, 8 Apr 2021 15:13:23 +0300 Subject: [PATCH] show catalog categories Signed-off-by: Jari Kolehmainen --- src/common/catalog-entities/index.ts | 1 + .../catalog-entities/kubernetes-cluster.ts | 4 ++ src/common/catalog-entities/web-link.ts | 65 +++++++++++++++++++ src/common/catalog-entity.ts | 1 + src/renderer/api/catalog-entity-registry.ts | 9 ++- .../+catalog/catalog-entity.store.ts | 17 ++++- src/renderer/components/+catalog/catalog.scss | 11 ++-- src/renderer/components/+catalog/catalog.tsx | 47 ++++++++++++-- 8 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 src/common/catalog-entities/web-link.ts diff --git a/src/common/catalog-entities/index.ts b/src/common/catalog-entities/index.ts index 1e6254a472..a42e68606d 100644 --- a/src/common/catalog-entities/index.ts +++ b/src/common/catalog-entities/index.ts @@ -1 +1,2 @@ export * from "./kubernetes-cluster"; +export * from "./web-link"; diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index f3f4c6b56b..78b99d3f5c 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -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()); diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts new file mode 100644 index 0000000000..b1ef05cc26 --- /dev/null +++ b/src/common/catalog-entities/web-link.ts @@ -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()); diff --git a/src/common/catalog-entity.ts b/src/common/catalog-entity.ts index 318311339b..8a00297d3c 100644 --- a/src/common/catalog-entity.ts +++ b/src/common/catalog-entity.ts @@ -16,6 +16,7 @@ export interface CatalogCategory { kind: string; } } + getId: () => string; } export type CatalogEntityMetadata = { diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index 31bf3902c6..afed7a88cf 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -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(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); diff --git a/src/renderer/components/+catalog/catalog-entity.store.ts b/src/renderer/components/+catalog/catalog-entity.store.ts index 96893b3f30..2c867ca02b 100644 --- a/src/renderer/components/+catalog/catalog-entity.store.ts +++ b/src/renderer/components/+catalog/catalog-entity.store.ts @@ -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 { + @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() { diff --git a/src/renderer/components/+catalog/catalog.scss b/src/renderer/components/+catalog/catalog.scss index 680d588844..302175d1f8 100644 --- a/src/renderer/components/+catalog/catalog.scss +++ b/src/renderer/components/+catalog/catalog.scss @@ -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; } } diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 6f6ce7f164..46e10d49e5 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -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 ( + +
Catalog
+ { this.categories.map((category, index) => { + return ; + })} +
+ ); + } + @autobind() renderItemMenu(item: CatalogEntityItem) { const onOpen = async () => { @@ -120,14 +153,18 @@ export class Catalog extends React.Component { } return ( - + item.name, [sortBy.source]: (item: CatalogEntityItem) => item.source,