@atlaskit/avatar-group
Version:
An avatar group displays a number of avatars grouped together in a stack or grid.
340 lines (333 loc) • 16.9 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
var _bindEventListener = require("bind-event-listener");
var _avatar = _interopRequireDefault(require("@atlaskit/avatar"));
var _keycodes = require("@atlaskit/ds-lib/keycodes");
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
var _useFocusEvent = _interopRequireDefault(require("@atlaskit/ds-lib/use-focus-event"));
var _useId = require("@atlaskit/ds-lib/use-id");
var _menu = require("@atlaskit/menu");
var _motion = require("@atlaskit/motion");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _popup = _interopRequireDefault(require("@atlaskit/popup"));
var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
var _avatarGroupItem = _interopRequireDefault(require("./avatar-group-item"));
var _avatarGroupTopLayer = require("./avatar-group-top-layer");
var _getOverrides2 = require("./get-overrides");
var _grid = _interopRequireDefault(require("./grid"));
var _focusManager = _interopRequireDefault(require("./internal/components/focus-manager"));
var _popupAvatarGroup = _interopRequireDefault(require("./internal/components/popup-avatar-group"));
var _moreIndicator = _interopRequireDefault(require("./more-indicator"));
var _stack = _interopRequireDefault(require("./stack"));
var _utils = require("./utils");
var _excluded = ["aria-controls", "aria-expanded", "aria-haspopup", "onClick"];
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var MAX_COUNT = {
grid: 11,
stack: 5
};
/**
* __Avatar group__
*
* An avatar group displays a number of avatars grouped together in a stack or grid.
*
* - [Examples](https://atlassian.design/components/avatar-group/examples)
* - [Code](https://atlassian.design/components/avatar-group/code)
* - [Usage](https://atlassian.design/components/avatar-group/usage)
*/
var AvatarGroup = function AvatarGroup(_ref) {
var _ref$appearance = _ref.appearance,
appearance = _ref$appearance === void 0 ? 'stack' : _ref$appearance,
moreIndicatorAppearance = _ref.moreIndicatorAppearance,
_ref$avatar = _ref.avatar,
avatar = _ref$avatar === void 0 ? _avatar.default : _ref$avatar,
borderColor = _ref.borderColor,
boundariesElement = _ref.boundariesElement,
data = _ref.data,
isTooltipDisabled = _ref.isTooltipDisabled,
maxCount = _ref.maxCount,
onAvatarClick = _ref.onAvatarClick,
onMoreClick = _ref.onMoreClick,
overrides = _ref.overrides,
_ref$showMoreButtonPr = _ref.showMoreButtonProps,
showMoreButtonProps = _ref$showMoreButtonPr === void 0 ? {} : _ref$showMoreButtonPr,
_ref$size = _ref.size,
size = _ref$size === void 0 ? 'medium' : _ref$size,
testId = _ref.testId,
_ref$label = _ref.label,
label = _ref$label === void 0 ? 'avatar group' : _ref$label,
moreIndicatorLabel = _ref.moreIndicatorLabel,
_ref$tooltipPosition = _ref.tooltipPosition,
tooltipPosition = _ref$tooltipPosition === void 0 ? 'bottom' : _ref$tooltipPosition,
_ref$shouldPopupRende = _ref.shouldPopupRenderToParent,
shouldPopupRenderToParent = _ref$shouldPopupRende === void 0 ? false : _ref$shouldPopupRende;
var _useState = (0, _react.useState)(false),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
isTriggeredUsingKeyboard = _useState2[0],
setTriggeredUsingKeyboard = _useState2[1];
var _useState3 = (0, _react.useState)(false),
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
isOpen = _useState4[0],
setIsOpen = _useState4[1];
var onClose = (0, _react.useCallback)(function () {
return setIsOpen(false);
}, []);
var handleTriggerClicked = (0, _react.useCallback)(function (event) {
var _ref2 = event,
clientX = _ref2.clientX,
clientY = _ref2.clientY,
type = _ref2.type;
// Hitting enter/space is registered as a click with both clientX and clientY === 0
if (type === 'keydown' || clientX === 0 || clientY === 0) {
setTriggeredUsingKeyboard(true);
}
setIsOpen(function (isOpen) {
return !isOpen;
});
}, []);
var _useFocus = (0, _useFocusEvent.default)(),
isFocused = _useFocus.isFocused,
bindFocus = _useFocus.bindFocus;
var _getOverrides = (0, _getOverrides2.getOverrides)(overrides),
avatarGroupItemOverrides = _getOverrides.AvatarGroupItem,
avatarOverrides = _getOverrides.Avatar,
moreIndicatorOverrides = _getOverrides.MoreIndicator;
// When a trigger is focused, we want to open the popup
// the user presses the DownArrow.
// Skipped when top-layer is enabled — DropdownMenu/top-layer handles
// ArrowDown-to-open and arrow key navigation internally.
(0, _react.useEffect)(function () {
// Set initial value if popup is closed
if (!isOpen) {
setTriggeredUsingKeyboard(false);
}
// Top-layer path: ArrowDown-to-open is handled by the menu's
// own focus management, so skip the parent's ArrowDown handler.
if ((0, _platformFeatureFlags.fg)('platform-dst-top-layer')) {
return _noop.default;
}
// Only need to listen for keydown when focused
if (!isFocused) {
return _noop.default;
}
// Being safe: we don't want to open the popup if it is already open
// Note: This shouldn't happen as the trigger should not be able to get focus
if (isOpen) {
return _noop.default;
}
(0, _bindEventListener.bind)(window, {
type: 'keydown',
listener: function openOnKeyDown(e) {
if (e.key === _keycodes.KEY_DOWN) {
// prevent page scroll
e.preventDefault();
handleTriggerClicked(e);
}
}
});
var unbind = function unbind() {
(0, _bindEventListener.bind)(window, {
type: 'keydown',
listener: function openOnKeyDown(e) {
if (e.key === _keycodes.KEY_DOWN) {
// prevent page scroll
e.preventDefault();
handleTriggerClicked(e);
}
}
});
};
return unbind;
}, [isFocused, isOpen, handleTriggerClicked]);
function renderMoreDropdown(max, total, labelId) {
if (total <= max) {
return null;
}
var renderMoreButton = function renderMoreButton(_ref3) {
var ariaControls = _ref3['aria-controls'],
ariaExpanded = _ref3['aria-expanded'],
ariaHasPopup = _ref3['aria-haspopup'],
onClick = _ref3.onClick,
props = (0, _objectWithoutProperties2.default)(_ref3, _excluded);
return moreIndicatorOverrides.render(_moreIndicator.default, _objectSpread(_objectSpread({
buttonProps: showMoreButtonProps,
borderColor: borderColor,
count: total - max,
size: size,
testId: testId && "".concat(testId, "--overflow-menu--trigger"),
isActive: isOpen,
moreIndicatorLabel: moreIndicatorLabel,
'aria-controls': ariaControls,
'aria-expanded': ariaExpanded,
'aria-haspopup': ariaHasPopup,
onClick: onClick
}, props), (0, _platformFeatureFlags.fg)('jira-ai-agent-stack') && {
appearance: moreIndicatorAppearance
}));
};
// bail if the consumer wants to handle onClick
if (typeof onMoreClick === 'function') {
return renderMoreButton({
onClick: onMoreClick
});
}
if ((0, _platformFeatureFlags.fg)('platform-dst-top-layer')) {
return /*#__PURE__*/_react.default.createElement(_avatarGroupTopLayer.MoreDropdownTopLayer, {
isOpen: isOpen,
onClose: onClose,
isTriggeredUsingKeyboard: isTriggeredUsingKeyboard,
data: data,
max: max
// eslint-disable-next-line @repo/internal/react/no-unsafe-overrides
,
overrides: overrides,
onAvatarClick: onAvatarClick,
testId: testId,
labelId: labelId,
renderMoreButton: renderMoreButton,
handleTriggerClicked: handleTriggerClicked,
bindFocus: bindFocus
});
}
// split boundariesElement into `boundary` and `rootBoundary` props for Popup
var boundary = boundariesElement === 'scrollParent' ? 'clippingParents' : undefined;
var rootBoundary = function () {
if (boundariesElement === 'scrollParent') {
return undefined;
}
return boundariesElement === 'window' ? 'document' : 'viewport';
}();
var avatarComponent = avatar && (0, _platformFeatureFlags.fg)('platform-avatar-group-pass-avatar-to-item') ? avatar : undefined;
return /*#__PURE__*/_react.default.createElement(_popup.default, {
isOpen: isOpen,
onClose: onClose,
placement: "bottom-end",
boundary: boundary,
rootBoundary: rootBoundary,
shouldFlip: true,
zIndex: 510,
shouldRenderToParent: shouldPopupRenderToParent,
content: function content(_ref4) {
var setInitialFocusRef = _ref4.setInitialFocusRef;
return /*#__PURE__*/_react.default.createElement(_focusManager.default, null, /*#__PURE__*/_react.default.createElement(_popupAvatarGroup.default, {
onClick: function onClick(e) {
return e.stopPropagation();
},
minWidth: 250,
maxHeight: 300,
setInitialFocusRef: isTriggeredUsingKeyboard ? setInitialFocusRef : undefined
}, /*#__PURE__*/_react.default.createElement(_menu.Section, {
titleId: labelId,
testId: "".concat(testId, "--section")
}, data.slice(max).map(function (avatarData, index) {
return avatarGroupItemOverrides.render(_avatarGroupItem.default, {
avatar: avatarData,
avatarComponent: avatarComponent,
onAvatarClick: onAvatarClick,
avatarOverrides: avatarOverrides,
testId: testId && "".concat(testId, "--avatar-group-item-").concat(index + max),
index: index + max
},
// This index holds the true index,
// adding up the index of non-overflowed avatars and overflowed avatars.
index + max);
}))));
},
trigger: function trigger(triggerProps) {
return (0, _platformFeatureFlags.fg)('platform-dst-motion-uplift') ? /*#__PURE__*/_react.default.createElement(_motion.Motion, {
enteringAnimation: "var(--ds-avatar-enter, 150ms cubic-bezier(0.4, 1, 0.6, 1) ScaleIn80to100, 150ms cubic-bezier(0.4, 1, 0.6, 1) FadeIn0to100)",
exitingAnimation: "var(--ds-avatar-exit, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) ScaleOut100to80, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)"
}, renderMoreButton(_objectSpread(_objectSpread(_objectSpread({}, triggerProps), bindFocus), {}, {
onClick: handleTriggerClicked
}))) : renderMoreButton(_objectSpread(_objectSpread(_objectSpread({}, triggerProps), bindFocus), {}, {
onClick: handleTriggerClicked
}));
},
testId: testId && "".concat(testId, "--overflow-menu")
});
}
var max = maxCount === undefined || maxCount === 0 ? MAX_COUNT[appearance] : maxCount;
var total = data.length;
var maxAvatar = total > max ? max - 1 : max;
var groupId = (0, _useId.useId)();
return appearance === 'stack' ? /*#__PURE__*/_react.default.createElement(_stack.default, {
id: groupId,
testId: testId && "".concat(testId, "--avatar-group"),
"aria-label": label,
size: size
}, data.slice(0, maxAvatar).map(function (avatarData, idx) {
var callback = avatarData.onClick || onAvatarClick;
var finalAvatar = avatarOverrides.render(avatar, _objectSpread(_objectSpread({}, avatarData), {}, {
size: size,
borderColor: borderColor || avatarData.borderColor,
testId: testId && "".concat(testId, "--avatar-").concat(idx),
onClick: callback ? function (event, analyticsEvent) {
callback(event, analyticsEvent, idx);
} : undefined,
stackIndex: max - idx
}), idx);
if ((0, _platformFeatureFlags.fg)('platform-dst-motion-uplift')) {
return /*#__PURE__*/_react.default.createElement(_motion.Motion, {
enteringAnimation: "var(--ds-avatar-enter, 150ms cubic-bezier(0.4, 1, 0.6, 1) ScaleIn80to100, 150ms cubic-bezier(0.4, 1, 0.6, 1) FadeIn0to100)",
exitingAnimation: "var(--ds-avatar-exit, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) ScaleOut100to80, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)",
key: (0, _utils.composeUniqueKey)(avatarData, idx)
}, !isTooltipDisabled && !avatarData.isDisabled ? /*#__PURE__*/_react.default.createElement(_tooltip.default, {
content: avatarData.name,
testId: testId && "".concat(testId, "--tooltip-").concat(idx),
position: tooltipPosition
}, finalAvatar) : finalAvatar);
} else {
return !isTooltipDisabled && !avatarData.isDisabled ? /*#__PURE__*/_react.default.createElement(_tooltip.default, {
key: (0, _utils.composeUniqueKey)(avatarData, idx),
content: avatarData.name,
testId: testId && "".concat(testId, "--tooltip-").concat(idx),
position: tooltipPosition
}, finalAvatar) : finalAvatar;
}
}), renderMoreDropdown(+maxAvatar, total, groupId)) : /*#__PURE__*/_react.default.createElement(_grid.default, {
id: groupId,
testId: testId && "".concat(testId, "--avatar-group"),
"aria-label": label
}, data.slice(0, maxAvatar).map(function (avatarData, idx) {
var callback = avatarData.onClick || onAvatarClick;
var finalAvatar = avatarOverrides.render(avatar, _objectSpread(_objectSpread({}, avatarData), {}, {
size: size,
borderColor: borderColor || avatarData.borderColor,
testId: testId && "".concat(testId, "--avatar-").concat(idx),
onClick: callback ? function (event, analyticsEvent) {
callback(event, analyticsEvent, idx);
} : undefined,
stackIndex: max - idx
}), idx);
if ((0, _platformFeatureFlags.fg)('platform-dst-motion-uplift')) {
return /*#__PURE__*/_react.default.createElement(_motion.Motion, {
enteringAnimation: "var(--ds-avatar-enter, 150ms cubic-bezier(0.4, 1, 0.6, 1) ScaleIn80to100, 150ms cubic-bezier(0.4, 1, 0.6, 1) FadeIn0to100)",
exitingAnimation: "var(--ds-avatar-exit, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) ScaleOut100to80, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)",
key: (0, _utils.composeUniqueKey)(avatarData, idx)
}, !isTooltipDisabled && !avatarData.isDisabled ? /*#__PURE__*/_react.default.createElement(_tooltip.default, {
content: avatarData.name,
testId: testId && "".concat(testId, "--tooltip-").concat(idx),
position: tooltipPosition
}, finalAvatar) : finalAvatar);
} else {
return !isTooltipDisabled && !avatarData.isDisabled ? /*#__PURE__*/_react.default.createElement(_tooltip.default, {
key: (0, _utils.composeUniqueKey)(avatarData, idx),
content: avatarData.name,
testId: testId && "".concat(testId, "--tooltip-").concat(idx),
position: tooltipPosition
}, finalAvatar) : finalAvatar;
}
}), renderMoreDropdown(+maxAvatar, total, groupId));
};
var _default = exports.default = AvatarGroup;