UNPKG

@octopusdeploy/design-system-components

Version:
111 lines (110 loc) 5.45 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AvatarStack = AvatarStack; 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"); const Avatar_1 = require("../Avatar"); /** * AvatarStack Component * Displays multiple avatars in a horizontal stack with overlap. * Limits the number of rendered avatars to 3. * Automatically detects avatar types and ensures consistency (either all user OR all entity types, never both). * Note: The title prop is required for all avatars in the stack to ensure proper accessibility. * * @param {AvatarProps} props - The properties for the avatar stack component. * @param {AvatarProps[]} props.avatars - Array of avatar props to render in the stack (size will be set by AvatarStack, title is required for AvatarStack usage). * @param {string} props.href - The URL to navigate to when the avatar stack is clicked. * @returns {React.ReactElement} A React element representing the avatar stack. */ function AvatarStack({ avatars, href }) { if (avatars.length === 1) { return (0, jsx_runtime_1.jsx)(Avatar_1.Avatar, { ...avatars[0], size: "medium", href: href }); } if (href) { return ((0, jsx_runtime_1.jsx)("a", { href: href, className: rootAvatarStackStyles, children: (0, jsx_runtime_1.jsx)(AvatarStackItems, { avatars: avatars }) })); } return ((0, jsx_runtime_1.jsx)("ul", { className: rootAvatarStackStyles, children: (0, jsx_runtime_1.jsx)(AvatarStackItems, { avatars: avatars }) })); } function isRoundedAvatar(avatarProps) { return avatarProps.shape === "rounded"; } function AvatarStackItems({ avatars }) { const avatarCount = avatars.length > 3 ? `+${avatars.length - 3}` : null; const avatarStackItems = avatars && avatars.slice(0, 3).map((avatar, index) => ((0, jsx_runtime_1.jsx)("li", { className: (0, css_1.cx)(avatarWrapperStyles, { [roundedAvatarStackItemStyles]: isRoundedAvatar(avatars[0]) }), children: (0, jsx_runtime_1.jsx)(Avatar_1.Avatar, { ...avatar, size: "medium" }) }, index))); return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [avatarStackItems, avatarCount && ((0, jsx_runtime_1.jsxs)("p", { className: countTextStyles, children: [avatarCount, " ", (0, jsx_runtime_1.jsx)("span", { className: hiddenFromScreenReadersStyles, children: "more" })] }))] })); } // Note: The design and code differ slightly because the avatar stack // renders differently in Figma compared to code, even with matching pixel values. // To compensate, we increased the border width to 4, which removes the need for padding. const rootAvatarStackStyles = (0, css_1.css)({ display: "grid", gridTemplateColumns: "repeat(4, auto)", alignItems: "center", justifyItems: "center", position: "relative", isolation: "isolate", borderRadius: design_system_tokens_1.borderRadius.small, width: "fit-content", boxSizing: "border-box", textDecoration: "none", "&[href]:hover": { cursor: "pointer", backgroundColor: design_system_tokens_1.themeTokens.color.avatarStack.background.hover, "& > li": { border: `${design_system_tokens_1.borderWidth[4]} solid ${design_system_tokens_1.themeTokens.color.avatarStack.background.hover}`, }, "& > p": { textDecoration: "underline", textDecorationColor: design_system_tokens_1.themeTokens.color.text.link.hover, color: design_system_tokens_1.themeTokens.color.text.link.hover, }, }, "&[href]:focus-visible": { outline: `${design_system_tokens_1.borderWidth[2]} solid ${design_system_tokens_1.themeTokens.color.border.selected}`, outlineOffset: "0", backgroundColor: design_system_tokens_1.themeTokens.color.avatarStack.background.hover, "& > li": { border: `${design_system_tokens_1.borderWidth[4]} solid ${design_system_tokens_1.themeTokens.color.avatarStack.background.hover}`, }, }, }); const avatarWrapperStyles = (0, css_1.css)({ position: "relative", border: `${design_system_tokens_1.borderWidth[4]} solid ${design_system_tokens_1.themeTokens.color.avatarStack.border}`, backgroundColor: design_system_tokens_1.themeTokens.color.background.primary.default, zIndex: 1, listStyle: "none", margin: 0, padding: 0, borderRadius: design_system_tokens_1.borderRadius.circle, boxSizing: "border-box", "&:not(:first-child)": { marginLeft: `-${design_system_tokens_1.space[12]}`, }, "&:nth-child(1)": { zIndex: 3, }, "&:nth-child(2)": { zIndex: 2, }, "&:nth-child(3)": { zIndex: 1, }, }); const roundedAvatarStackItemStyles = (0, css_1.css)({ borderRadius: design_system_tokens_1.borderRadius.large, }); const countTextStyles = (0, css_1.css)({ margin: 0, marginLeft: design_system_tokens_1.space[4], // Extra right margin added to balance the visual illusion of a larger left side in the avatar stack marginRight: design_system_tokens_1.space[6], color: design_system_tokens_1.themeTokens.color.text.link.default, font: design_system_tokens_1.text.body.regular.medium, }); const hiddenFromScreenReadersStyles = (0, css_1.css)({ ...utils_1.accessibilityStyles.hidden, });