/** * Copyright (c) 2021 OpenLens Authors * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import "./kube-object-list-layout.scss"; import React from "react"; import { computed, makeObservable, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { cssNames, Disposer } from "../../utils"; import type { KubeObject } from "../../../common/k8s-api/kube-object"; import { ItemListLayout, ItemListLayoutProps } from "../item-object-list/item-list-layout"; import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; import { KubeObjectMenu } from "../kube-object-menu"; import { kubeWatchApi } from "../../../common/k8s-api/kube-watch-api"; import { clusterContext } from "../context"; import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; import { ResourceKindMap, ResourceNames } from "../../utils/rbac"; import { kubeSelectedUrlParam, toggleDetails } from "../kube-detail-params"; import { Icon } from "../icon"; import { TooltipPosition } from "../tooltip"; export interface KubeObjectListLayoutProps extends ItemListLayoutProps { store: KubeObjectStore; dependentStores?: KubeObjectStore[]; subscribeStores?: boolean; } const defaultProps: Partial> = { onDetails: (item: KubeObject) => toggleDetails(item.selfLink), subscribeStores: true, }; @observer export class KubeObjectListLayout extends React.Component> { static defaultProps = defaultProps as object; constructor(props: KubeObjectListLayoutProps) { super(props); makeObservable(this); } @observable loadErrors: string[] = []; @computed get selectedItem() { return this.props.store.getByPath(kubeSelectedUrlParam.get()); } componentDidMount() { const { store, dependentStores = [], subscribeStores } = this.props; const stores = Array.from(new Set([store, ...dependentStores])); const reactions: Disposer[] = [ reaction(() => clusterContext.contextNamespaces.slice(), () => { // clear load errors this.loadErrors.length = 0; }), ]; if (subscribeStores) { reactions.push( kubeWatchApi.subscribeStores(stores, { preload: true, namespaces: clusterContext.contextNamespaces, onLoadFailure: error => this.loadErrors.push(String(error)), }), ); } disposeOnUnmount(this, reactions); } renderLoadErrors() { if (this.loadErrors.length === 0) { return null; } return ( {this.loadErrors.map((error, index) =>

{error}

)} ), preferredPositions: TooltipPosition.BOTTOM, }} /> ); } render() { const { className, customizeHeader, store, items = store.contextItems, ...layoutProps } = this.props; const placeholderString = ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind; return ( ({ filters: ( <> {filters} {store.api.isNamespaced && } ), searchProps: { ...searchProps, placeholder: `Search ${placeholderString}...`, }, info: ( <> {info} {this.renderLoadErrors()} ), ...headerPlaceHolders, }), ...[customizeHeader].flat(), ]} renderItemMenu={item => } /> ); } }