diff --git a/src/renderer/components/+catalog/catalog.module.css b/src/renderer/components/+catalog/catalog.module.css new file mode 100644 index 0000000000..b8db97108e --- /dev/null +++ b/src/renderer/components/+catalog/catalog.module.css @@ -0,0 +1,87 @@ +/** + * 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. + */ + +.iconCell { + max-width: 40px; + display: flex; + align-items: center; +} + +.nameCell { +} + +.sourceCell { + max-width: 100px; +} + +.statusCell { + max-width: 100px; +} + +.connected { + color: var(--colorSuccess); +} + +.disconnected { + color: var(--halfGray); +} + +.labelsCell { + overflow-x: scroll; + text-overflow: unset; +} + +.labelsCell::-webkit-scrollbar { + display: none; +} + +.badge { + overflow: unset; + text-overflow: unset; + max-width: unset; +} + +.badge:not(:first-child) { + margin-left: 0.5em; +} + +.catalogIcon { + font-size: 10px; + -webkit-font-smoothing: auto; +} + +.tab { + @apply px-8 py-4; +} + +.tab:hover { + background-color: var(--sidebarItemHoverBackground); + --color-active: var(--textColorTertiary); +} + +.tab:hover::after { + display: none; +} + +.activeTab, .activeTab:hover { + background-color: var(--blue); + --color-active: white; +} \ No newline at end of file diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index cd04892745..d2eccf5783 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -19,15 +19,14 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./catalog.scss"; +import styles from "./catalog.module.css"; + import React from "react"; import { disposeOnUnmount, observer } from "mobx-react"; import { ItemListLayout } from "../item-object-list"; import { action, makeObservable, observable, reaction, when } from "mobx"; import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store"; import { navigate } from "../../navigation"; -import { kebabCase } from "lodash"; -import { PageLayout } from "../layout/page-layout"; import { MenuItem, MenuActions } from "../menu"; import { CatalogEntityContextMenu, CatalogEntityContextMenuContext, catalogEntityRunContext } from "../../api/catalog-entity"; import { Badge } from "../badge"; @@ -40,6 +39,8 @@ import type { RouteComponentProps } from "react-router"; import type { ICatalogViewRouteParam } from "./catalog.route"; import { Notifications } from "../notifications"; import { Avatar } from "../avatar/avatar"; +import { MainLayout } from "../layout/main-layout"; +import { cssNames } from "../../utils"; enum sortBy { name = "name", @@ -137,13 +138,13 @@ export class Catalog extends React.Component { renderNavigation() { return ( -
Catalog
-
+
{ this.categories.map(category => ( @@ -152,6 +153,7 @@ export class Catalog extends React.Component { key={category.getId()} label={category.metadata.name} data-testid={`${category.getId()}-tab`} + className={cssNames(styles.tab, { [styles.activeTab]: this.activeTab == category.getId() })} /> )) } @@ -192,7 +194,7 @@ export class Catalog extends React.Component { colorHash={`${item.name}-${item.source}`} width={24} height={24} - className="catalogIcon" + className={styles.catalogIcon} /> ); } @@ -203,46 +205,44 @@ export class Catalog extends React.Component { } return ( - - item.name, - [sortBy.source]: (item: CatalogEntityItem) => item.source, - [sortBy.status]: (item: CatalogEntityItem) => item.phase, - }} - searchFilters={[ - (entity: CatalogEntityItem) => entity.searchFields, - ]} - renderTableHeader={[ - { title: "", className: "icon" }, - { title: "Name", className: "name", sortBy: sortBy.name }, - { title: "Source", className: "source", sortBy: sortBy.source }, - { title: "Labels", className: "labels" }, - { title: "Status", className: "status", sortBy: sortBy.status }, - ]} - renderTableContents={(item: CatalogEntityItem) => [ - this.renderIcon(item), - item.name, - item.source, - item.labels.map((label) => ), - { title: item.phase, className: kebabCase(item.phase) } - ]} - onDetails={(item: CatalogEntityItem) => this.onDetails(item) } - renderItemMenu={this.renderItemMenu} - /> + +
+ item.name, + [sortBy.source]: (item: CatalogEntityItem) => item.source, + [sortBy.status]: (item: CatalogEntityItem) => item.phase, + }} + searchFilters={[ + (entity: CatalogEntityItem) => entity.searchFields, + ]} + renderTableHeader={[ + { title: "", className: styles.iconCell }, + { title: "Name", className: styles.nameCell, sortBy: sortBy.name }, + { title: "Source", className: styles.sourceCell, sortBy: sortBy.source }, + { title: "Labels", className: styles.labelsCell }, + { title: "Status", className: styles.statusCell, sortBy: sortBy.status }, + ]} + renderTableContents={(item: CatalogEntityItem) => [ + this.renderIcon(item), + item.name, + item.source, + item.labels.map((label) => ), + { title: item.phase, className: cssNames({ [styles.connected]: item.phase == "connected" }) } + ]} + onDetails={(item: CatalogEntityItem) => this.onDetails(item) } + renderItemMenu={this.renderItemMenu} + /> +
-
+ ); } } diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index 89a04a58d3..948fec03ce 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -71,6 +71,8 @@ import { CommandContainer } from "./command-palette/command-container"; import { KubeObjectStore } from "../kube-object.store"; import { clusterContext } from "./context"; import { namespaceStore } from "./+namespaces/namespace.store"; +import { Sidebar } from "./layout/sidebar"; +import { Dock } from "./dock"; @observer export class App extends React.Component { @@ -176,7 +178,7 @@ export class App extends React.Component { return ( - + } footer={}> diff --git a/src/renderer/components/item-object-list/item-list-layout.scss b/src/renderer/components/item-object-list/item-list-layout.scss index a91e2a2a18..2609d0b323 100644 --- a/src/renderer/components/item-object-list/item-list-layout.scss +++ b/src/renderer/components/item-object-list/item-list-layout.scss @@ -28,7 +28,7 @@ padding: var(--flex-gap); .title { - color: $textColorPrimary; + color: var(--textColorTertiary); } .info-panel { diff --git a/src/renderer/components/layout/main-layout.module.css b/src/renderer/components/layout/main-layout.module.css new file mode 100644 index 0000000000..bd517263f9 --- /dev/null +++ b/src/renderer/components/layout/main-layout.module.css @@ -0,0 +1,48 @@ +/** + * 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. + */ + +.mainLayout { + display: grid; + grid-template-areas: + "sidebar contents" + "sidebar footer"; + grid-template-rows: [contents] 1fr [footer] auto; + grid-template-columns: [sidebar] var(--sidebar-width) [contents] 1fr; + width: 100%; + z-index: 1; +} + +.sidebar { + grid-area: sidebar; + background: var(--sidebarBackground); + width: var(--sidebar-width); +} + +.contents { + grid-area: contents; + overflow: auto; +} + +.footer { + position: relative; + grid-area: footer; + min-width: 0; /* restrict size when overflow content (e.g. tabs scrolling) */ +} \ No newline at end of file diff --git a/src/renderer/components/layout/main-layout.tsx b/src/renderer/components/layout/main-layout.tsx index 455f0dfdfc..a85e90b158 100755 --- a/src/renderer/components/layout/main-layout.tsx +++ b/src/renderer/components/layout/main-layout.tsx @@ -19,73 +19,53 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./main-layout.scss"; +import styles from "./main-layout.module.css"; import React from "react"; import { observer } from "mobx-react"; -import { getHostedCluster } from "../../../common/cluster-store"; import { cssNames } from "../../utils"; -import { Dock } from "../dock"; import { ErrorBoundary } from "../error-boundary"; import { ResizeDirection, ResizeGrowthDirection, ResizeSide, ResizingAnchor } from "../resizing-anchor"; -import { MainLayoutHeader } from "./main-layout-header"; -import { Sidebar } from "./sidebar"; import { sidebarStorage } from "./sidebar-storage"; -export interface MainLayoutProps { - className?: any; +interface Props { + sidebar: React.ReactNode; + className?: string; footer?: React.ReactNode; - headerClass?: string; - footerClass?: string; } @observer -export class MainLayout extends React.Component { - onSidebarCompactModeChange = () => { - sidebarStorage.merge(draft => { - draft.compact = !draft.compact; - }); - }; - +export class MainLayout extends React.Component { onSidebarResize = (width: number) => { sidebarStorage.merge({ width }); }; render() { - const cluster = getHostedCluster(); - const { onSidebarCompactModeChange, onSidebarResize } = this; - const { className, headerClass, footer, footerClass, children } = this.props; - const { compact, width: sidebarWidth } = sidebarStorage.get(); + const { onSidebarResize } = this; + const { className, footer, children, sidebar } = this.props; + const { width: sidebarWidth } = sidebarStorage.get(); const style = { "--sidebar-width": `${sidebarWidth}px` } as React.CSSProperties; - if (!cluster) { - return null; // fix: skip render when removing active (visible) cluster - } - return ( -
- - - +
-
+
{children} -
+
-
{footer ?? }
+
{footer}
); } diff --git a/src/renderer/themes/lens-dark.json b/src/renderer/themes/lens-dark.json index 7949eb14fa..822a5f1c36 100644 --- a/src/renderer/themes/lens-dark.json +++ b/src/renderer/themes/lens-dark.json @@ -26,6 +26,7 @@ "sidebarLogoBackground": "#414448", "sidebarActiveColor": "#ffffff", "sidebarSubmenuActiveColor": "#ffffff", + "sidebarItemHoverBackground": "#3a3e44", "buttonPrimaryBackground": "#3d90ce", "buttonDefaultBackground": "#414448", "buttonLightBackground": "#f1f1f1", diff --git a/src/renderer/themes/lens-light.json b/src/renderer/themes/lens-light.json index d3b5938de3..8d9339d71a 100644 --- a/src/renderer/themes/lens-light.json +++ b/src/renderer/themes/lens-light.json @@ -27,6 +27,7 @@ "sidebarActiveColor": "#ffffff", "sidebarSubmenuActiveColor": "#3d90ce", "sidebarBackground": "#e8e8e8", + "sidebarItemHoverBackground": "#f0f2f5", "buttonPrimaryBackground": "#3d90ce", "buttonDefaultBackground": "#414448", "buttonLightBackground": "#f1f1f1",