UNPKG

@atlaskit/avatar

Version:

An avatar is a visual representation of a user or entity.

195 lines (189 loc) • 7.7 kB
/* avatar.tsx generated by @compiled/babel-plugin v3.0.2 */ import "./avatar.compiled.css"; import * as React from 'react'; import { ax, ix } from "@compiled/react/runtime"; import { forwardRef, isValidElement, useCallback, useEffect, useRef } from 'react'; import { useAnalyticsEvents } from '@atlaskit/analytics-next/useAnalyticsEvents'; import { useId } from '@atlaskit/ds-lib/use-id'; import { fg } from '@atlaskit/platform-feature-flags/fg'; import { AvatarContent } from './avatar-content'; import AvatarImage from './internal/avatar-image'; import { AvatarContentContext } from './internal/content-context'; import { EnsureIsInsideAvatarContext } from './internal/ensure-is-inside-avatar-context'; import getCustomElement from './internal/get-custom-element'; import PresenceWrapper from './internal/presence-wrapper'; import StatusWrapper from './internal/status-wrapper'; import { useAvatarContext } from './use-avatar-context'; const packageName = "@atlaskit/avatar"; const packageVersion = "30.0.2"; const containerStyles = null; const updatedHexagonNegativeMarginMap = { xxsmall: "_6rth1b3j _1pfh1b3j", small: "_6rth1tv6 _1pfh1tv6", medium: "_6rth1lmm _1pfh1lmm", large: "_6rth1wmd _1pfh1wmd", xlarge: "_6rth130q _1pfh130q", xxlarge: "_6rthyzhl _1pfhyzhl" }; const normalizeAvatarSize = size => size === 'xsmall' && !fg('platform_design-system-team_avatar-remove-xsmall') ? 'xxsmall' : size; /** * Popup trigger ARIA attributes are optional. Use `aria-controls` to identify * the popup and `aria-expanded` to announce whether it is open. An * `aria-haspopup` value indicating a popup renders Avatar as a button, * even when `onClick` is absent. */ // eslint-disable-next-line @repo/internal/react/consistent-types-definitions /** * __Avatar__ * * An avatar is a visual representation of a user or entity. * * - [Examples](https://atlassian.design/components/avatar/examples) * - [Code](https://atlassian.design/components/avatar/code) * - [Usage](https://atlassian.design/components/avatar/usage) */ const Avatar = /*#__PURE__*/forwardRef(({ analyticsContext, appearance = 'circle', UNSAFE_isUpdatedGeometry, label, borderColor, children, href, isDisabled, name, onClick, presence, size: sizeProp, src, stackIndex, status, target, testId, as: AvatarContainer = 'div', imgLoading, 'aria-controls': ariaControls, 'aria-expanded': ariaExpanded, 'aria-haspopup': ariaHasPopup }, ref) => { const { createAnalyticsEvent } = useAnalyticsEvents(); const context = useAvatarContext(); const size = normalizeAvatarSize(sizeProp || (context === null || context === void 0 ? void 0 : context.size) || 'medium'); const isUpdatedHexagonGeometry = appearance === 'hexagon' && Boolean(UNSAFE_isUpdatedGeometry) && size !== 'UNSAFE_xsmall'; const customPresenceNode = /*#__PURE__*/isValidElement(presence) ? presence : null; const customStatusNode = /*#__PURE__*/isValidElement(status) ? status : null; const isValidIconSize = size !== 'xxlarge' && size !== 'xxsmall' && size !== 'xsmall'; // Presence/status indicators are only defined for `IndicatorSizeType` // (small, medium, large, xlarge). Compute `indicatorSize` only for sizes // that support an indicator (`isValidIconSize`), so the value is // structurally guaranteed to be a valid `IndicatorSizeType` rather than // relying on the guard at each call site. The `UNSAFE_xsmall` (20px) size // reuses the `small` indicator sizing as its nearest supported neighbor; // unsupported sizes (`xxsmall`/`xsmall`/`xxlarge`) fall back to `small` and are never // actually rendered because `isPresence`/`isStatus` also guard on // `isValidIconSize`. const indicatorSize = !isValidIconSize ? 'small' : size === 'UNSAFE_xsmall' ? 'small' : size; const lastAnalytics = useRef(analyticsContext); const labelId = useId(); useEffect(() => { lastAnalytics.current = analyticsContext; }, [analyticsContext]); const onClickHandler = useCallback(event => { if (isDisabled || typeof onClick !== 'function') { return; } const analyticsEvent = createAnalyticsEvent({ action: 'clicked', actionSubject: 'avatar', attributes: { componentName: 'avatar', packageName, packageVersion } }); /** * To avoid wrapping this component in AnalyticsContext we manually * push the parent context's meta data into the context. */ const context = { componentName: 'avatar', packageName, packageVersion, ...lastAnalytics.current }; analyticsEvent.context.push(context); /** * Replicating the logic in the `withAnalyticsEvents` HOC. */ const clone = analyticsEvent.clone(); if (clone) { clone.fire('atlaskit'); } onClick(event, analyticsEvent); }, [createAnalyticsEvent, isDisabled, onClick]); const isPresence = isValidIconSize && presence && !status; const isStatus = isValidIconSize && status; // add presence or status to the label by default if presence and status are passed as a string // if status or presence are nodes this is not added to the label by default const defaultLabel = [name, isStatus && !customStatusNode && `(${status})`, isPresence && !customPresenceNode && `(${presence})`].filter(Boolean).join(' '); const isInteractive = onClick || href || isDisabled || ariaHasPopup; const containerShouldBeImage = Boolean(!isInteractive && defaultLabel); return /*#__PURE__*/React.createElement(EnsureIsInsideAvatarContext.Provider, { value: true }, /*#__PURE__*/React.createElement(AvatarContainer, { "data-testid": testId, role: containerShouldBeImage ? 'img' : undefined, "aria-labelledby": containerShouldBeImage ? labelId : undefined, style: { zIndex: stackIndex }, className: ax(["_12ji1r31 _1qu2glyw _12y3idpf _1e0c1o8l _kqswh2mm", isUpdatedHexagonGeometry && updatedHexagonNegativeMarginMap[size]]) }, /*#__PURE__*/React.createElement(AvatarContentContext.Provider, { value: { as: getCustomElement(isDisabled, href, onClick, ariaHasPopup), appearance, UNSAFE_isUpdatedGeometry: isUpdatedHexagonGeometry, borderColor, href, isDisabled, label: label || defaultLabel, onClick: isInteractive ? onClickHandler : undefined, ref, size, stackIndex, target, testId: testId ? `${testId}--inner` : undefined, 'aria-controls': ariaControls, 'aria-expanded': ariaExpanded, 'aria-haspopup': ariaHasPopup, avatarImage: /*#__PURE__*/React.createElement(AvatarImage, { alt: !containerShouldBeImage && src ? name : undefined, src: src, appearance: appearance, UNSAFE_isUpdatedGeometry: isUpdatedHexagonGeometry, size: size, testId: testId, imgLoading: imgLoading }) } }, children || /*#__PURE__*/React.createElement(AvatarContent, null)), isPresence && /*#__PURE__*/React.createElement(PresenceWrapper, { appearance: appearance, size: indicatorSize, presence: typeof presence === 'string' ? presence : undefined, testId: testId }, customPresenceNode), isStatus && /*#__PURE__*/React.createElement(StatusWrapper, { appearance: appearance, size: indicatorSize, borderColor: borderColor, status: typeof status === 'string' ? status : undefined, testId: testId }, customStatusNode), containerShouldBeImage ? /*#__PURE__*/React.createElement("span", { "data-testid": testId && `${testId}--label`, id: labelId, hidden: true }, defaultLabel) : undefined)); }); Avatar.displayName = 'Avatar'; export default Avatar;