From 42cce4f667c2a4101ee49c202dc7c08d3bf1d756 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Mon, 24 May 2021 12:25:37 +0300 Subject: [PATCH] Create generic Avatar component Signed-off-by: Alex Andreev --- src/renderer/components/+catalog/catalog.scss | 19 ++-- src/renderer/components/+catalog/catalog.tsx | 15 +-- src/renderer/components/avatar/avatar.tsx | 92 +++++++++++++++++++ .../components/hotbar/hotbar-icon.scss | 3 - .../components/hotbar/hotbar-icon.tsx | 56 ++--------- 5 files changed, 114 insertions(+), 71 deletions(-) create mode 100644 src/renderer/components/avatar/avatar.tsx diff --git a/src/renderer/components/+catalog/catalog.scss b/src/renderer/components/+catalog/catalog.scss index 02db315b24..e76606567b 100644 --- a/src/renderer/components/+catalog/catalog.scss +++ b/src/renderer/components/+catalog/catalog.scss @@ -71,18 +71,8 @@ .TableCell.icon { max-width: 40px; - .HotbarIcon { - --size: 24px; - position: relative; - top: -3px; - .MuiAvatar-root { - font-size: 8px; - border-radius: 3px; - &:hover { - box-shadow: none; - } - } - } + display: flex; + align-items: center; } .TableCell.source { @@ -118,4 +108,9 @@ } } } + + .catalogIcon { + font-size: 10px; + -webkit-font-smoothing: auto; + } } diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 15b89f792c..7f5d10b015 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -40,7 +40,7 @@ import { CatalogAddButton } from "./catalog-add-button"; import type { RouteComponentProps } from "react-router"; import type { ICatalogViewRouteParam } from "./catalog.route"; import { Notifications } from "../notifications"; -import { HotbarIcon } from "../hotbar/hotbar-icon"; +import { Avatar } from "../avatar/avatar"; enum sortBy { name = "name", @@ -183,12 +183,13 @@ export class Catalog extends React.Component { } return ( - <> - - + ); } diff --git a/src/renderer/components/avatar/avatar.tsx b/src/renderer/components/avatar/avatar.tsx new file mode 100644 index 0000000000..2cf78866f4 --- /dev/null +++ b/src/renderer/components/avatar/avatar.tsx @@ -0,0 +1,92 @@ +/** + * 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 React, { DOMAttributes } from "react"; +import randomColor from "randomcolor"; +import GraphemeSplitter from "grapheme-splitter"; +import { Avatar as MaterialAvatar, AvatarTypeMap } from "@material-ui/core"; +import { iter } from "../../utils"; + +interface Props extends DOMAttributes, Partial { + title: string; + colorHash?: string; + width?: number; + height?: number; + src?: string; + className?: string; +} + +function getNameParts(name: string): string[] { + const byWhitespace = name.split(/\s+/); + + if (byWhitespace.length > 1) { + return byWhitespace; + } + + const byDashes = name.split(/[-_]+/); + + if (byDashes.length > 1) { + return byDashes; + } + + return name.split(/@+/); +} + +function getIconString(title: string) { + if (!title) { + return "??"; + } + + const [rawFirst, rawSecond, rawThird] = getNameParts(title); + const splitter = new GraphemeSplitter(); + const first = splitter.iterateGraphemes(rawFirst); + const second = rawSecond ? splitter.iterateGraphemes(rawSecond): first; + const third = rawThird ? splitter.iterateGraphemes(rawThird) : iter.newEmpty(); + + return [ + ...iter.take(first, 1), + ...iter.take(second, 1), + ...iter.take(third, 1), + ].filter(Boolean).join(""); +} + +export function Avatar(props: Props) { + const { title, src, width = 32, height = 32, colorHash, ...settings } = props; + + const generateAvatarStyle = (): React.CSSProperties => { + return { + backgroundColor: randomColor({ seed: colorHash, luminosity: "dark" }), + width, + height, + textTransform: "uppercase" + }; + }; + + return ( + + {getIconString(title)} + + ); +} diff --git a/src/renderer/components/hotbar/hotbar-icon.scss b/src/renderer/components/hotbar/hotbar-icon.scss index 8008f0ad6c..d573e86eb5 100644 --- a/src/renderer/components/hotbar/hotbar-icon.scss +++ b/src/renderer/components/hotbar/hotbar-icon.scss @@ -32,8 +32,6 @@ } .HotbarIcon { - --size: 37px; - border-radius: 6px; user-select: none; cursor: pointer; @@ -69,7 +67,6 @@ &.active { box-shadow: 0 0 0px 3px var(--clusterMenuBackground), 0 0 0px 6px var(--textColorAccent); - transition: all 0s 0.8s; } &:hover { diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx index 7c567ff681..f2193bfb8b 100644 --- a/src/renderer/components/hotbar/hotbar-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-icon.tsx @@ -22,16 +22,14 @@ import "./hotbar-icon.scss"; import React, { DOMAttributes, useState } from "react"; -import { Avatar } from "@material-ui/core"; -import randomColor from "randomcolor"; -import GraphemeSplitter from "grapheme-splitter"; import type { CatalogEntityContextMenu } from "../../../common/catalog"; -import { cssNames, IClassName, iter } from "../../utils"; +import { cssNames, IClassName } from "../../utils"; import { ConfirmDialog } from "../confirm-dialog"; import { Menu, MenuItem } from "../menu"; import { MaterialTooltip } from "../+catalog/material-tooltip/material-tooltip"; import { observer } from "mobx-react"; +import { Avatar } from "../avatar/avatar"; interface Props extends DOMAttributes { uid: string; @@ -44,12 +42,6 @@ interface Props extends DOMAttributes { disabled?: boolean; } -function generateAvatarStyle(seed: string): React.CSSProperties { - return { - "backgroundColor": randomColor({ seed, luminosity: "dark" }) - }; -} - function onMenuItemClick(menuItem: CatalogEntityContextMenu) { if (menuItem.confirm) { ConfirmDialog.open({ @@ -67,22 +59,6 @@ function onMenuItemClick(menuItem: CatalogEntityContextMenu) { } } -function getNameParts(name: string): string[] { - const byWhitespace = name.split(/\s+/); - - if (byWhitespace.length > 1) { - return byWhitespace; - } - - const byDashes = name.split(/[-_]+/); - - if (byDashes.length > 1) { - return byDashes; - } - - return name.split(/@+/); -} - export const HotbarIcon = observer(({menuItems = [], ...props}: Props) => { const { uid, title, active, className, source, disabled, onMenuOpen, children, ...rest } = props; const id = `hotbarIcon-${uid}`; @@ -92,36 +68,18 @@ export const HotbarIcon = observer(({menuItems = [], ...props}: Props) => { setMenuOpen(!menuOpen); }; - const getIconString = () => { - if (!title) { - return "??"; - } - - const [rawFirst, rawSecond, rawThird] = getNameParts(title); - const splitter = new GraphemeSplitter(); - const first = splitter.iterateGraphemes(rawFirst); - const second = rawSecond ? splitter.iterateGraphemes(rawSecond): first; - const third = rawThird ? splitter.iterateGraphemes(rawThird) : iter.newEmpty(); - - return [ - ...iter.take(first, 1), - ...iter.take(second, 1), - ...iter.take(third, 1), - ].filter(Boolean).join(""); - }; - return (
- {getIconString()} - + width={40} + height={40} + /> {children}