{
-
+
Lens
{
Cluster}
- icon={}
+ icon={}
/>
Nodes}
- icon={}
+ icon={}
/>
{
routePath={workloadsRoute.path}
subMenus={Workloads.tabRoutes}
text={Workloads}
- icon={}
+ icon={}
/>
{
routePath={configRoute.path}
subMenus={Config.tabRoutes}
text={Configuration}
- icon={}
+ icon={}
/>
{
routePath={networkRoute.path}
subMenus={Network.tabRoutes}
text={Network}
- icon={}
+ icon={}
/>
{
url={storageURL({ query })}
routePath={storageRoute.path}
subMenus={Storage.tabRoutes}
- icon={}
+ icon={}
text={Storage}
/>
}
+ icon={}
text={Namespaces}
/>
}
+ icon={}
text={Events}
/>
{
url={appsURL({ query })}
subMenus={Apps.tabRoutes}
routePath={appsRoute.path}
- icon={}
+ icon={}
text={Apps}
/>
{
url={usersManagementURL({ query })}
routePath={usersManagementRoute.path}
subMenus={UserManagement.tabRoutes}
- icon={}
+ icon={}
text={Access Control}
/>
}
+ icon={}
text={Custom Resources}
>
{this.renderCustomResources()}
@@ -186,7 +186,7 @@ export class Sidebar extends React.Component {
- )
+ );
}
}
@@ -203,7 +203,10 @@ interface SidebarNavItemProps {
const navItemStorage = createStorage<[string, boolean][]>("sidebar_menu_item", []);
const navItemState = observable.map
(navItemStorage.get());
-reaction(() => [...navItemState], value => navItemStorage.set(value));
+reaction(
+ () => [...navItemState],
+ (value) => navItemStorage.set(value)
+);
@observer
class SidebarNavItem extends React.Component {
@@ -216,15 +219,15 @@ class SidebarNavItem extends React.Component {
toggleSubMenu = () => {
navItemState.set(this.props.id, !this.isExpanded);
- }
+ };
isActive = () => {
const { routePath, url } = this.props;
const { pathname } = navigation.location;
return !!matchPath(pathname, {
- path: routePath || url
+ path: routePath || url,
});
- }
+ };
render() {
const { id, isHidden, subMenus = [], icon, text, url, children, className } = this.props;
@@ -239,10 +242,7 @@ class SidebarNavItem extends React.Component {
{icon}
{text}
-
+
{subMenus.map(({ title, url }) => (
@@ -252,18 +252,18 @@ class SidebarNavItem extends React.Component {
))}
{React.Children.toArray(children).map((child: React.ReactElement) => {
return React.cloneElement(child, {
- className: cssNames(child.props.className, { visible: this.isExpanded })
+ className: cssNames(child.props.className, { visible: this.isExpanded }),
});
})}
- )
+ );
}
return (
{icon}
{text}
- )
+ );
}
}
diff --git a/src/renderer/components/layout/tab-layout.scss b/src/renderer/components/layout/tab-layout.scss
new file mode 100755
index 0000000000..e8b62558d7
--- /dev/null
+++ b/src/renderer/components/layout/tab-layout.scss
@@ -0,0 +1,25 @@
+
+.TabLayout {
+ display: contents;
+
+ > .Tabs {
+ grid-area: tabs;
+ background: $layoutTabsBackground;
+ }
+
+
+ main {
+ @include custom-scrollbar;
+ $spacing: $margin * 2;
+
+ .theme-light & {
+ @include custom-scrollbar(dark);
+ }
+
+ grid-area: main;
+ overflow-y: scroll; // always reserve space for scrollbar (17px)
+ overflow-x: auto;
+ margin: $spacing;
+ margin-right: 0;
+ }
+}
\ No newline at end of file
diff --git a/src/renderer/components/layout/tab-layout.tsx b/src/renderer/components/layout/tab-layout.tsx
new file mode 100644
index 0000000000..82c0cac6cf
--- /dev/null
+++ b/src/renderer/components/layout/tab-layout.tsx
@@ -0,0 +1,45 @@
+import "./tab-layout.scss";
+
+import React, { ReactNode } from "react";
+import { matchPath, RouteProps } from "react-router-dom";
+import { observer } from "mobx-react";
+import { cssNames } from "../../utils";
+import { Tab, Tabs } from "../tabs";
+import { ErrorBoundary } from "../error-boundary";
+import { navigate, navigation } from "../../navigation";
+import { getHostedCluster } from "../../../common/cluster-store";
+
+export interface TabRoute extends RouteProps {
+ title: React.ReactNode;
+ url: string;
+}
+
+interface Props {
+ children: ReactNode;
+ className?: any;
+ tabs?: TabRoute[];
+ contentClass?: string;
+}
+
+export const TabLayout = observer(({ className, contentClass, tabs, children }: Props) => {
+ const routePath = navigation.location.pathname;
+ const cluster = getHostedCluster();
+ if (!cluster) {
+ return null; // fix: skip render when removing active (visible) cluster
+ }
+ return (
+
+ {tabs && (
+ navigate(url)}>
+ {tabs.map(({ title, path, url, ...routeProps }) => {
+ const isActive = !!matchPath(routePath, { path, ...routeProps });
+ return ;
+ })}
+
+ )}
+
+ {children}
+
+
+ );
+});