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