UNPKG

@octopusdeploy/design-system-components

Version:
144 lines (143 loc) • 6.62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.avatarSizeMap = void 0; exports.Avatar = Avatar; const jsx_runtime_1 = require("react/jsx-runtime"); const css_1 = require("@emotion/css"); const design_system_tokens_1 = require("@octopusdeploy/design-system-tokens"); const utils_1 = require("./utils"); /** * Wraps the avatar in a clickable link or a static container. * Displays an "edit" overlay if `hasEditOverlay` is true. * * @param {object} props - The component props. * @param {string} [props.href] - The URL to navigate to when the avatar is clicked. * @param {string} props.className - The CSS class for styling the wrapper. * @param {React.ReactNode} props.children - The avatar content. * @param {boolean} [props.hasEditOverlay] - Whether to show the "edit" overlay. * @param {string} [props.title] - An optional title for the avatar, useful for when no additional text is provided will provide a native tooltip on hover * @returns {React.ReactElement} The wrapper element. */ function AvatarWrapper({ href, className, children, hasEditOverlay, title }) { if (href) { return ((0, jsx_runtime_1.jsxs)("a", { href: href, className: className, title: title, children: [children, hasEditOverlay && (0, jsx_runtime_1.jsx)("p", { className: (0, css_1.cx)("avatarWrapperEditOverlay", avatarEditStyles), children: "Edit" })] })); } return ((0, jsx_runtime_1.jsx)("div", { className: className, title: title, children: children })); } /** * The base component for rendering an avatar. * Handles image loading, fallback rendering, and wrapper behavior. * * @param {string} [props.className] - Additional CSS styles to apply to the avatar. * @param {object} props - The component props. * @param {AvatarSize} props.size - The size of the avatar (small, medium, large). * @param {string} [props.src] - The source URL for the avatar image. * @param {string} [props.alt] - The alt text for the avatar image. * @param {string} [props.href] - The URL to navigate to when the avatar is clicked. * @param {boolean} [props.hasEditOverlay] - Whether to show the "edit" overlay. * @param {boolean} [props.isMuted] - Whether the avatar is visually isMuted. * @param {React.ReactNode} props.fallback - The fallback content if the image fails to load. * @param {AvatarType} props.type - The type of the avatar (user, space, project, etc.). * @param {string} [props.title] - An optional title for the avatar, useful for when no additional text is provided will provide a native tooltip on hover * @returns {React.ReactElement} The rendered avatar. */ function Avatar({ className, shape, size, src, srcSet, alt, href, hasEditOverlay, isMuted, fallback, title }) { const canLoadImage = (0, utils_1.useCanLoadImage)(src || ""); const optionalImageProps = { ...(srcSet && { srcSet }), }; return ((0, jsx_runtime_1.jsx)(AvatarWrapper, { href: href, className: (0, css_1.cx)(rootAvatarStyles, sizeStyleMap[size], shapeStyleMap[shape], className, { [isMutedAvatarStyles]: isMuted }), hasEditOverlay: hasEditOverlay, title: title, children: src && canLoadImage === true ? (0, jsx_runtime_1.jsx)("img", { className: avatarImageStyles, src: src, ...optionalImageProps, alt: alt }) : fallback })); } // Shared avatar styles const rootAvatarStyles = (0, css_1.css)({ display: "flex", flexShrink: 0, userSelect: "none", overflow: "hidden", alignItems: "center", justifyContent: "center", containerType: "inline-size", textDecoration: "none", color: design_system_tokens_1.themeTokens.color.avatar.text, fontFamily: design_system_tokens_1.fontFamily.default, position: "relative", transition: "background-color 150ms ease-in-out", backgroundColor: design_system_tokens_1.themeTokens.color.background.secondary.default, // Note: Have to set the color for hover and focus because of the global LESS files for text colors // If we dont set it will inherit from the LESS styles instead of the component "&[href]:hover": { color: design_system_tokens_1.themeTokens.color.avatar.text, }, "&[href]:focus-visible": { color: design_system_tokens_1.themeTokens.color.avatar.text, outline: `2px solid ${design_system_tokens_1.themeTokens.color.border.selected}`, outlineOffset: "2px", }, "&[href]:hover, &[href]:focus-visible": { backgroundColor: design_system_tokens_1.themeTokens.color.background.tertiary, }, // Display Edit text on hover or focus "&[href]:hover > .avatarWrapperEditOverlay, &[href]:focus-visible > .avatarWrapperEditOverlay": { opacity: "1", transition: "opacity 150ms ease-in-out", }, // Different background colour for images so if the image is transparent the background is less intrusive "& img": { backgroundColor: design_system_tokens_1.themeTokens.color.avatar.background.image.default, transition: "background-color 150ms ease-in-out", }, "&[href]:hover img": { backgroundColor: design_system_tokens_1.themeTokens.color.avatar.background.image.hover, }, }); // State Styles const isMutedAvatarStyles = (0, css_1.css)({ opacity: 0.4, }); const avatarEditStyles = (0, css_1.css)({ color: design_system_tokens_1.themeTokens.color.avatar.text, position: "absolute", inset: 0, display: "grid", placeContent: "center", overflow: "hidden", backgroundColor: design_system_tokens_1.themeTokens.color.overlay.primary, fontSize: "34cqi", containerType: "inline-size", margin: 0, opacity: 0, }); // Variant styles const avatarInitialsFallback = (0, css_1.css)({ fontSize: "44cqi", display: "block", margin: 0, color: design_system_tokens_1.themeTokens.color.avatar.text, }); const avatarImageStyles = (0, css_1.css)({ objectFit: "scale-down", width: "100%", height: "100%", aspectRatio: "1 / 1", }); // Maps avatar sizes to their pixel dimensions. exports.avatarSizeMap = { small: 20, medium: 36, large: 48, }; const shapeStyleMap = { rounded: (0, css_1.css)({ borderRadius: design_system_tokens_1.borderRadius.small }), circle: (0, css_1.css)({ borderRadius: design_system_tokens_1.borderRadius.circle }), }; const sizeStyleMap = { small: getAvatarSizeStyles("small"), medium: getAvatarSizeStyles("medium"), large: getAvatarSizeStyles("large"), }; function getAvatarSizeStyles(size) { return (0, css_1.css)({ height: `${exports.avatarSizeMap[size]}px`, width: `${exports.avatarSizeMap[size]}px`, }); }