@octopusdeploy/design-system-components
Version:
The design systems component library.
144 lines (143 loc) • 6.62 kB
JavaScript
;
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`,
});
}