@primer/react
Version:
An implementation of GitHub's Primer Design System using React
159 lines (149 loc) • 6.05 kB
JavaScript
var clsx = require('clsx');
var React = require('react');
var Avatar = require('../Avatar/Avatar.js');
var useResponsiveValue = require('../hooks/useResponsiveValue.js');
var AvatarStack_module = require('./AvatarStack.module.css.js');
var hasInteractiveNodes = require('../internal/utils/hasInteractiveNodes.js');
var BoxWithFallback = require('../internal/components/BoxWithFallback.js');
var jsxRuntime = require('react/jsx-runtime');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var React__default = /*#__PURE__*/_interopDefault(React);
const transformChildren = (children, shape) => {
return React__default.default.Children.map(children, child => {
if (! /*#__PURE__*/React__default.default.isValidElement(child)) return child;
return /*#__PURE__*/React__default.default.cloneElement(child, {
...child.props,
square: shape === 'square' ? true : undefined,
className: clsx.clsx(child.props.className, 'pc-AvatarItem', AvatarStack_module.AvatarItem)
});
});
};
const AvatarStackBody = ({
disableExpand,
hasInteractiveChildren,
stackContainer,
children
}) => {
return /*#__PURE__*/jsxRuntime.jsx("div", {
"data-disable-expand": disableExpand ? '' : undefined,
className: clsx.clsx({
'pc-AvatarStack--disableExpand': disableExpand
}, 'pc-AvatarStackBody', AvatarStack_module.AvatarStackBody),
tabIndex: !hasInteractiveChildren && !disableExpand ? 0 : undefined,
ref: stackContainer,
children: children
});
};
AvatarStackBody.displayName = "AvatarStackBody";
const AvatarStack = ({
children,
variant = 'cascade',
shape = 'circle',
alignRight,
disableExpand,
size,
className,
style,
sx: sxProp
}) => {
const [hasInteractiveChildren, setHasInteractiveChildren] = React.useState(false);
const stackContainer = React.useRef(null);
const count = React__default.default.Children.count(children);
const getAvatarChildSizes = () => {
const avatarSizeMap = {
narrow: [],
regular: [],
wide: []
};
return React__default.default.Children.toArray(children).reduce((acc, child) => {
// if child is not an Avatar, return the default avatar sizes from the accumulator
if (! /*#__PURE__*/React__default.default.isValidElement(child)) return acc;
for (const responsiveKey of Object.keys(avatarSizeMap)) {
// if the child has responsive `size` prop values, push the value to the appropriate viewport property in the avatarSizeMap
if (useResponsiveValue.isResponsiveValue(child.props.size)) {
avatarSizeMap[responsiveKey].push(child.props.size[responsiveKey] || Avatar.DEFAULT_AVATAR_SIZE);
}
// otherwise, the size is a number (or undefined), so push the value to all viewport properties in the avatarSizeMap
else {
avatarSizeMap[responsiveKey].push(child.props.size || Avatar.DEFAULT_AVATAR_SIZE);
}
// set the smallest size in each viewport property as the value for that viewport property in the accumulator
acc[responsiveKey] = Math.min(...avatarSizeMap[responsiveKey]);
}
return acc;
}, {
narrow: Avatar.DEFAULT_AVATAR_SIZE,
regular: Avatar.DEFAULT_AVATAR_SIZE,
wide: Avatar.DEFAULT_AVATAR_SIZE
});
};
const childSizes = getAvatarChildSizes();
React.useEffect(() => {
if (stackContainer.current) {
const interactiveChildren = () => {
setHasInteractiveChildren(hasInteractiveNodes.hasInteractiveNodes(stackContainer.current));
};
const observer = new MutationObserver(interactiveChildren);
observer.observe(stackContainer.current, {
childList: true
});
// Call on initial render, then call it again only if there's a mutation
interactiveChildren();
return () => {
observer.disconnect();
};
}
}, []);
const getResponsiveAvatarSizeStyles = () => {
// if there is no size set on the AvatarStack, use the `size` props of the Avatar children to set the `--avatar-stack-size` CSS variable
if (!size) {
return {
'--stackSize-narrow': `${childSizes.narrow}px`,
'--stackSize-regular': `${childSizes.regular}px`,
'--stackSize-wide': `${childSizes.wide}px`
};
}
// if the `size` prop is set and responsive, set the `--avatar-stack-size` CSS variable for each viewport
if (useResponsiveValue.isResponsiveValue(size)) {
return {
'--stackSize-narrow': `${size.narrow || Avatar.DEFAULT_AVATAR_SIZE}px`,
'--stackSize-regular': `${size.regular || Avatar.DEFAULT_AVATAR_SIZE}px`,
'--stackSize-wide': `${size.wide || Avatar.DEFAULT_AVATAR_SIZE}px`
};
}
// if the `size` prop is set and not responsive, it is a number, so we can just set the `--avatar-stack-size` CSS variable to that number
return {
'--avatar-stack-size': `${size}px`
};
};
return /*#__PURE__*/jsxRuntime.jsx(BoxWithFallback.BoxWithFallback, {
as: "span",
"data-variant": variant,
"data-shape": shape,
"data-avatar-count": count > 3 ? '3+' : count,
"data-align-right": alignRight ? '' : undefined,
"data-responsive": !size || useResponsiveValue.isResponsiveValue(size) ? '' : undefined,
className: clsx.clsx({
'pc-AvatarStack--variant': variant,
'pc-AvatarStack--shape': shape,
'pc-AvatarStack--two': count === 2,
'pc-AvatarStack--three': count === 3,
'pc-AvatarStack--three-plus': count > 3,
'pc-AvatarStack--right': alignRight
}, className, AvatarStack_module.AvatarStack),
style: {
...getResponsiveAvatarSizeStyles(),
...style
},
sx: sxProp,
children: /*#__PURE__*/jsxRuntime.jsxs(AvatarStackBody, {
disableExpand: disableExpand,
hasInteractiveChildren: hasInteractiveChildren,
stackContainer: stackContainer,
children: [' ', transformChildren(children, shape)]
})
});
};
AvatarStack.displayName = "AvatarStack";
module.exports = AvatarStack;
;