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 "./kubernetes-cluster";
export * from "./web-link";

View File

@ -96,6 +96,10 @@ export class KubernetesClusterCategory extends EventEmitter implements CatalogCa
kind: "KubernetesCluster" kind: "KubernetesCluster"
} }
}; };
getId() {
return `${this.spec.group}/${this.spec.names.kind}`;
}
} }
catalogCategoryRegistry.add(new KubernetesClusterCategory()); 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; kind: string;
} }
} }
getId: () => string;
} }
export type CatalogEntityMetadata = { export type CatalogEntityMetadata = {

View File

@ -1,6 +1,6 @@
import { action, observable } from "mobx"; import { action, observable } from "mobx";
import { broadcastMessage, subscribeToBroadcast } from "../../common/ipc"; 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 { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../common/catalog-category-registry";
import "../../common/catalog-entities"; import "../../common/catalog-entities";
@ -49,6 +49,13 @@ export class CatalogEntityRegistry {
return items as T[]; 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); 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 { catalogEntityRegistry } from "../../api/catalog-entity-registry";
import { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity"; import { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
import { ItemObject, ItemStore } from "../../item.store"; import { ItemObject, ItemStore } from "../../item.store";
import { autobind } from "../../utils"; import { autobind } from "../../utils";
import { CatalogCategory } from "../../../common/catalog-entity";
export class CatalogEntityItem implements ItemObject { export class CatalogEntityItem implements ItemObject {
constructor(public entity: CatalogEntity) {} constructor(public entity: CatalogEntity) {}
@ -55,13 +56,23 @@ export class CatalogEntityItem implements ItemObject {
@autobind() @autobind()
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> { export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
@observable activeCategory: CatalogCategory;
@computed get entities() { @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() { 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() { loadAll() {

View File

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

View File

@ -2,7 +2,7 @@ import "./catalog.scss";
import React from "react"; import React from "react";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { ItemListLayout } from "../item-object-list"; import { ItemListLayout } from "../item-object-list";
import { observable } from "mobx"; import { observable, reaction } from "mobx";
import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store"; import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
import { navigate } from "../../navigation"; import { navigate } from "../../navigation";
import { kebabCase } from "lodash"; import { kebabCase } from "lodash";
@ -16,17 +16,19 @@ import { addClusterURL } from "../+add-cluster";
import { autobind } from "../../utils"; import { autobind } from "../../utils";
import { Notifications } from "../notifications"; import { Notifications } from "../notifications";
import { ConfirmDialog } from "../confirm-dialog"; import { ConfirmDialog } from "../confirm-dialog";
import { Tab, Tabs } from "../tabs";
import { catalogCategoryRegistry } from "../../../common/catalog-category-registry";
enum sortBy { enum sortBy {
name = "name", name = "name",
source = "source", source = "source",
status = "status" status = "status"
} }
@observer @observer
export class Catalog extends React.Component { export class Catalog extends React.Component {
@observable private catalogEntityStore?: CatalogEntityStore; @observable private catalogEntityStore?: CatalogEntityStore;
@observable.deep private contextMenu: CatalogEntityContextMenuContext; @observable.deep private contextMenu: CatalogEntityContextMenuContext;
@observable activeTab: string;
async componentDidMount() { async componentDidMount() {
this.contextMenu = { this.contextMenu = {
@ -35,7 +37,13 @@ export class Catalog extends React.Component {
}; };
this.catalogEntityStore = new CatalogEntityStore(); this.catalogEntityStore = new CatalogEntityStore();
disposeOnUnmount(this, [ 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(() => { 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() @autobind()
renderItemMenu(item: CatalogEntityItem) { renderItemMenu(item: CatalogEntityItem) {
const onOpen = async () => { const onOpen = async () => {
@ -120,14 +153,18 @@ export class Catalog extends React.Component {
} }
return ( return (
<PageLayout className="CatalogPage" provideBackButtonNavigation={false}> <PageLayout
className="CatalogPage"
navigation={this.renderNavigation()}
provideBackButtonNavigation={false}
contentGaps={false}>
<ItemListLayout <ItemListLayout
renderHeaderTitle="Catalog"
isClusterScoped isClusterScoped
isSearchable={true} isSearchable={true}
isSelectable={false} isSelectable={false}
className="CatalogItemList" className="CatalogItemList"
store={this.catalogEntityStore} store={this.catalogEntityStore}
tableId="catalog-items"
sortingCallbacks={{ sortingCallbacks={{
[sortBy.name]: (item: CatalogEntityItem) => item.name, [sortBy.name]: (item: CatalogEntityItem) => item.name,
[sortBy.source]: (item: CatalogEntityItem) => item.source, [sortBy.source]: (item: CatalogEntityItem) => item.source,