From 03a57fc9c97548928233736006a0119139bba0d6 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Mon, 29 Nov 2021 15:33:45 +0300 Subject: [PATCH] Move HotbarIcon to css modules Signed-off-by: Alex Andreev --- src/renderer/components/+catalog/catalog.tsx | 2 +- .../components/avatar/avatar.module.css | 5 ++ src/renderer/components/avatar/avatar.tsx | 13 +++-- .../hotbar/hotbar-entity-icon.module.css | 56 +++++++++++++++++++ .../components/hotbar/hotbar-entity-icon.tsx | 55 ++++++++---------- .../components/hotbar/hotbar-icon.module.css | 43 ++++++++++++++ .../components/hotbar/hotbar-icon.scss | 18 +++--- .../components/hotbar/hotbar-icon.tsx | 50 +++++++---------- .../components/hotbar/hotbar-menu.scss | 2 +- 9 files changed, 165 insertions(+), 79 deletions(-) create mode 100644 src/renderer/components/hotbar/hotbar-entity-icon.module.css create mode 100644 src/renderer/components/hotbar/hotbar-icon.module.css diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 01b4bf0f1d..45b0844193 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -221,7 +221,7 @@ export class Catalog extends React.Component { className={styles.catalogAvatar} size={24} > - {item.entity.spec.icon?.material && } + {item.entity.spec.icon?.material && } {item.name} { +export interface AvatarProps extends HTMLAttributes { title: string; colorHash?: string; size?: number; src?: string; - className?: string; background?: string; variant?: "circle" | "rounded" | "square"; - imgProps?: React.ImgHTMLAttributes; + imgProps?: ImgHTMLAttributes; + disabled?: boolean; } function getNameParts(name: string): string[] { @@ -71,8 +71,8 @@ function getLabelFromTitle(title: string) { ].filter(Boolean).join(""); } -export function Avatar(props: Props) { - const { title, variant = "rounded", size = 32, colorHash, children, background, imgProps, src, className, ...rest } = props; +export function Avatar(props: AvatarProps) { + const { title, variant = "rounded", size = 32, colorHash, children, background, imgProps, src, className, disabled, ...rest } = props; const getBackgroundColor = () => { return background || randomColor({ seed: colorHash, luminosity: "dark" }); @@ -91,6 +91,7 @@ export function Avatar(props: Props) { className={cssNames(styles.Avatar, { [styles.circle]: variant == "circle", [styles.rounded]: variant == "rounded", + [styles.disabled]: disabled, }, className)} style={{ width: `${size}px`, height: `${size}px`, backgroundColor: getBackgroundColor() }} {...rest} diff --git a/src/renderer/components/hotbar/hotbar-entity-icon.module.css b/src/renderer/components/hotbar/hotbar-entity-icon.module.css new file mode 100644 index 0000000000..2c34f30fed --- /dev/null +++ b/src/renderer/components/hotbar/hotbar-entity-icon.module.css @@ -0,0 +1,56 @@ +/** + * 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. + */ + +.led { + position: absolute; + left: 3px; + top: 3px; + background-color: var(--layoutBackground); + border: 1px solid var(--clusterMenuBackground); + border-radius: 50%; + padding: 0px; + width: 8px; + height: 8px; + + &.online { + background-color: var(--primary); + box-shadow: 0 0 5px var(--clusterMenuBackground), 0 0 5px var(--primary); + } +} + +.badge { + position: absolute; + right: -2px; + bottom: -3px; + margin: -8px; + font-size: var(--font-size-small); + background: var(--clusterMenuBackground); + color: var(--textColorAccent); + padding: 0px; + border-radius: 50%; + border: 2px solid var(--clusterMenuBackground); + width: 15px; + height: 15px; + + svg { + width: 13px; + } +} diff --git a/src/renderer/components/hotbar/hotbar-entity-icon.tsx b/src/renderer/components/hotbar/hotbar-entity-icon.tsx index 8ab35ae6ae..e889306c41 100644 --- a/src/renderer/components/hotbar/hotbar-entity-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-entity-icon.tsx @@ -19,7 +19,9 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import React, { DOMAttributes } from "react"; +import styles from "./hotbar-entity-icon.module.css"; + +import React, { HTMLAttributes } from "react"; import { makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; @@ -32,10 +34,9 @@ import { Icon } from "../icon"; import { HotbarIcon } from "./hotbar-icon"; import { LensKubernetesClusterStatus } from "../../../common/catalog-entities/kubernetes-cluster"; -interface Props extends DOMAttributes { +interface Props extends HTMLAttributes { entity: CatalogEntity; index: number; - className?: IClassName; errorClass?: IClassName; add: (item: CatalogEntity, index: number) => void; remove: (uid: string) => void; @@ -55,7 +56,7 @@ export class HotbarEntityIcon extends React.Component { } get kindIcon() { - const className = "badge"; + const className = styles.badge; const category = catalogCategoryRegistry.getCategoryForEntity(this.props.entity); if (!category) { @@ -74,7 +75,7 @@ export class HotbarEntityIcon extends React.Component { return null; } - const className = cssNames("led", { online: this.props.entity.status.phase === LensKubernetesClusterStatus.CONNECTED }); // TODO: make it more generic + const className = cssNames(styles.led, { [styles.online]: this.props.entity.status.phase === LensKubernetesClusterStatus.CONNECTED }); // TODO: make it more generic return
; } @@ -83,34 +84,25 @@ export class HotbarEntityIcon extends React.Component { return catalogEntityRegistry.activeEntity?.metadata?.uid == item.getId(); } + async onMenuOpen() { + const menuItems: CatalogEntityContextMenu[] = []; + + menuItems.unshift({ + title: "Remove from Hotbar", + onClick: () => this.props.remove(this.props.entity.metadata.uid), + }); + + this.contextMenu.menuItems = menuItems; + + await this.props.entity.onContextMenuOpen(this.contextMenu); + } + render() { if (!this.contextMenu) { return null; } - const { - entity, errorClass, add, remove, - index, children, ...elemProps - } = this.props; - const className = cssNames("HotbarEntityIcon", this.props.className, { - interactive: true, - active: this.isActive(entity), - disabled: !entity, - }); - - const onOpen = async () => { - const menuItems: CatalogEntityContextMenu[] = []; - - menuItems.unshift({ - title: "Remove from Hotbar", - onClick: () => remove(entity.metadata.uid), - }); - - this.contextMenu.menuItems = menuItems; - - await entity.onContextMenuOpen(this.contextMenu); - }; - const isActive = this.isActive(entity); + const { entity, errorClass, add, remove, index, children, ...elemProps } = this.props; return ( { src={entity.spec.icon?.src} material={entity.spec.icon?.material} background={entity.spec.icon?.background} - className={className} - active={isActive} - onMenuOpen={onOpen} + className={this.props.className} + active={this.isActive(entity)} + onMenuOpen={() => this.onMenuOpen()} + disabled={!entity} menuItems={this.contextMenu.menuItems} tooltip={`${entity.metadata.name} (${entity.metadata.source})`} {...elemProps} diff --git a/src/renderer/components/hotbar/hotbar-icon.module.css b/src/renderer/components/hotbar/hotbar-icon.module.css new file mode 100644 index 0000000000..44346c7058 --- /dev/null +++ b/src/renderer/components/hotbar/hotbar-icon.module.css @@ -0,0 +1,43 @@ +/** + * 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. + */ + +.HotbarIcon { + --iconActiveShadow: 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px var(--textColorAccent); + --iconHoverShadow: 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px #ffffff50; + + display: flex; + cursor: pointer; + transition: none; + position: relative; + border-radius: var(--border-radius); + + &:not(.active):hover { + box-shadow: var(--iconHoverShadow); + } +} + +.contextMenuAvailable { + cursor: context-menu; +} + +.active { + box-shadow: var(--iconActiveShadow); +} \ No newline at end of file diff --git a/src/renderer/components/hotbar/hotbar-icon.scss b/src/renderer/components/hotbar/hotbar-icon.scss index e713b20432..c866ed6724 100644 --- a/src/renderer/components/hotbar/hotbar-icon.scss +++ b/src/renderer/components/hotbar/hotbar-icon.scss @@ -19,16 +19,16 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -.HotbarMenu { - .HotbarIconMenu { - left: 30px; - min-width: 250px; +// .HotbarMenu { +// .HotbarIconMenu { +// left: 30px; +// min-width: 250px; - li.MenuItem { - font-size: 0.9em; - } - } -} +// li.MenuItem { +// font-size: 0.9em; +// } +// } +// } .HotbarIcon { --iconActiveShadow: 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px var(--textColorAccent); diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx index cf75879392..485081e382 100644 --- a/src/renderer/components/hotbar/hotbar-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-icon.tsx @@ -19,32 +19,27 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./hotbar-icon.scss"; +import styles from "./hotbar-icon.module.css"; -import React, { DOMAttributes, useState } from "react"; +import React, { useState } from "react"; import type { CatalogEntityContextMenu } from "../../../common/catalog"; -import { cssNames, IClassName } from "../../utils"; +import { cssNames } from "../../utils"; import { ConfirmDialog } from "../confirm-dialog"; import { Menu, MenuItem } from "../menu"; import { observer } from "mobx-react"; -import { Avatar } from "../avatar/avatar"; +import { Avatar, AvatarProps } from "../avatar/avatar"; import { Icon } from "../icon"; import { Tooltip } from "../tooltip"; -export interface HotbarIconProps extends DOMAttributes { +export interface Props extends AvatarProps { uid: string; - title: string; source: string; - src?: string; material?: string; onMenuOpen?: () => void; - className?: IClassName; active?: boolean; menuItems?: CatalogEntityContextMenu[]; disabled?: boolean; - size?: number; - background?: string; tooltip?: string; } @@ -65,7 +60,7 @@ function onMenuItemClick(menuItem: CatalogEntityContextMenu) { } } -export const HotbarIcon = observer(({ menuItems = [], size = 40, tooltip, ...props }: HotbarIconProps) => { +export const HotbarIcon = observer(({ menuItems = [], size = 40, tooltip, ...props }: Props) => { const { uid, title, src, material, active, className, source, disabled, onMenuOpen, onClick, children, ...rest } = props; const id = `hotbarIcon-${uid}`; const [menuOpen, setMenuOpen] = useState(false); @@ -75,32 +70,25 @@ export const HotbarIcon = observer(({ menuItems = [], size = 40, tooltip, ...pro }; return ( -
0 })}> +
0 })}> {tooltip && {tooltip}} -
{ - if (!disabled) { - onClick?.(event); - } - }} + title={title} + colorHash={`${title}-${source}`} + className={cssNames({ [styles.active]: active })} + disabled={disabled} + size={size} + src={src} + onClick={(event) => !disabled && onClick?.(event)} > - - {material && } - - {children} -
+ {material && } + + {children} div { animation: click .1s; } }