diff options
Diffstat (limited to 'server/sonar-web/design-system/src/components/Avatar.tsx')
-rw-r--r-- | server/sonar-web/design-system/src/components/Avatar.tsx | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/server/sonar-web/design-system/src/components/Avatar.tsx b/server/sonar-web/design-system/src/components/Avatar.tsx new file mode 100644 index 00000000000..8b454295681 --- /dev/null +++ b/server/sonar-web/design-system/src/components/Avatar.tsx @@ -0,0 +1,117 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import styled from '@emotion/styled'; +import { ReactEventHandler, useState } from 'react'; +import tw from 'twin.macro'; +import { themeBorder, themeColor } from '../helpers/theme'; +import { GenericAvatar } from './GenericAvatar'; + +type Size = 'xs' | 'sm' | 'md' | 'lg'; + +const sizeMap: Record<Size, number> = { + xs: 16, + sm: 24, + md: 40, + lg: 64, +}; + +interface AvatarProps { + border?: boolean; + className?: string; + enableGravatar?: boolean; + gravatarServerUrl?: string; + hash?: string; + name?: string; + organizationAvatar?: string; + organizationName?: string; + size?: Size; +} + +export function Avatar({ + className, + enableGravatar, + gravatarServerUrl, + hash, + name, + organizationAvatar, + organizationName, + size = 'sm', + border, +}: AvatarProps) { + const [imgError, setImgError] = useState(false); + const numberSize = sizeMap[size]; + const resolvedName = organizationName ?? name; + + const handleImgError: ReactEventHandler<HTMLImageElement> = () => { + setImgError(true); + }; + + if (!imgError) { + if (enableGravatar && gravatarServerUrl && hash) { + const url = gravatarServerUrl + .replace('{EMAIL_MD5}', hash) + .replace('{SIZE}', String(numberSize * 2)); + + return ( + <StyledAvatar + alt={resolvedName} + border={border} + className={className} + height={numberSize} + onError={handleImgError} + role="img" + src={url} + width={numberSize} + /> + ); + } + + if (resolvedName && organizationAvatar) { + return ( + <StyledAvatar + alt={resolvedName} + border={border} + className={className} + height={numberSize} + onError={handleImgError} + role="img" + src={organizationAvatar} + width={numberSize} + /> + ); + } + } + + if (!resolvedName) { + return <input className="sw-appearance-none" />; + } + + return <GenericAvatar className={className} name={resolvedName} size={numberSize} />; +} + +const StyledAvatar = styled.img<{ border?: boolean }>` + ${tw`sw-inline-flex`}; + ${tw`sw-items-center`}; + ${tw`sw-justify-center`}; + ${tw`sw-align-top`}; + ${tw`sw-rounded-1`}; + border: ${({ border }) => (border ? themeBorder('default', 'avatarBorder') : '')}; + background: ${themeColor('avatarBackground')}; +`; |