import type { TabLayoutRoute } from "./tab-layout"; import "./sidebar.scss"; import React from "react"; import { computed, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { NavLink } from "react-router-dom"; import { Trans } from "@lingui/macro"; import { createStorage, cssNames } from "../../utils"; import { Icon } from "../icon"; import { workloadsRoute, workloadsURL } from "../+workloads/workloads.route"; import { namespacesRoute, namespacesURL } from "../+namespaces/namespaces.route"; import { nodesRoute, nodesURL } from "../+nodes/nodes.route"; import { usersManagementRoute, usersManagementURL } from "../+user-management/user-management.route"; import { networkRoute, networkURL } from "../+network/network.route"; import { storageRoute, storageURL } from "../+storage/storage.route"; import { clusterRoute, clusterURL } from "../+cluster"; import { Config, configRoute, configURL } from "../+config"; import { eventRoute, eventsURL } from "../+events"; import { Apps, appsRoute, appsURL } from "../+apps"; import { namespaceStore } from "../+namespaces/namespace.store"; import { Workloads } from "../+workloads"; import { UserManagement } from "../+user-management"; import { Storage } from "../+storage"; import { Network } from "../+network"; import { crdStore } from "../+custom-resources/crd.store"; import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resources"; import { CustomResources } from "../+custom-resources/custom-resources"; import { isActiveRoute, navigation } from "../../navigation"; import { isAllowedResource } from "../../../common/rbac"; import { Spinner } from "../spinner"; import { clusterPageMenuRegistry, clusterPageRegistry, getExtensionPageUrl } from "../../../extensions/registries"; const SidebarContext = React.createContext({ pinned: false }); type SidebarContextValue = { pinned: boolean; }; interface Props { className?: string; isPinned: boolean; toggle(): void; } @observer export class Sidebar extends React.Component { async componentDidMount() { if (!crdStore.isLoaded && isAllowedResource("customresourcedefinitions")) { crdStore.loadAll(); } } renderCustomResources() { if (crdStore.isLoading) { return ; } return Object.entries(crdStore.groups).map(([group, crds]) => { const submenus: TabLayoutRoute[] = crds.map((crd) => { return { title: crd.getResourceKind(), component: CrdList, url: crd.getResourceUrl(), routePath: String(crdResourcesRoute.path), }; }); return ( ); }); } render() { const { toggle, isPinned, className } = this.props; const query = namespaceStore.getContextParams(); return (
Lens
Compact view} material={isPinned ? "keyboard_arrow_left" : "keyboard_arrow_right"} onClick={toggle} focusable={false} />
Cluster} icon={} /> Nodes} icon={} /> Workloads} icon={} /> Configuration} icon={} /> Network} icon={} /> } text={Storage} /> } text={Namespaces} /> } text={Events} /> } text={Apps} /> } text={Access Control} /> } text={Custom Resources} > {this.renderCustomResources()} {clusterPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => { const registeredPage = clusterPageRegistry.getByPageMenuTarget(target); if (!registeredPage) return; const { extensionId, id: pageId } = registeredPage; const pageUrl = getExtensionPageUrl({ extensionId, pageId, params: target.params }); const isActive = pageUrl === navigation.location.pathname; return ( } isActive={isActive} /> ); })}
); } } interface SidebarNavItemProps { url: string; text: React.ReactNode | string; className?: string; icon?: React.ReactNode; isHidden?: boolean; isActive?: boolean; subMenus?: TabLayoutRoute[]; testId?: string; // data-test-id="" property for integration tests } const navItemStorage = createStorage<[string, boolean][]>("sidebar_menu_item", []); const navItemState = observable.map(navItemStorage.get()); reaction(() => [...navItemState], (value) => navItemStorage.set(value)); @observer class SidebarNavItem extends React.Component { static contextType = SidebarContext; public context: SidebarContextValue; get itemId() { return this.props.url; } @computed get isExpanded() { return navItemState.get(this.itemId); } toggleSubMenu = () => { navItemState.set(this.itemId, !this.isExpanded); }; render() { const { isHidden, isActive, subMenus = [], icon, text, url, children, className, testId } = this.props; if (isHidden) { return null; } const extendedView = (subMenus.length > 0 || children) && this.context.pinned; if (extendedView) { return (
{icon} {text}
    {subMenus.map(({ title, url }) => ( {title} ))} {React.Children.toArray(children).map((child: React.ReactElement) => { return React.cloneElement(child, { className: cssNames(child.props.className, { visible: this.isExpanded }), }); })}
); } return ( isActive}> {icon} {text} ); } }