import * as React from 'react'; import { getIntrinsicElementProps, mergeCallbacks, useId, slot } from '@fluentui/react-utilities'; import { getInitials } from '../../utils/index'; import { PersonRegular } from '@fluentui/react-icons'; import { PresenceBadge } from '@fluentui/react-badge'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { useAvatarContext } from '../../contexts/AvatarContext'; export const DEFAULT_STRINGS = { active: 'active', inactive: 'inactive' }; export const useAvatar_unstable = (props, ref)=>{ const { dir } = useFluent(); const { shape: contextShape, size: contextSize } = useAvatarContext(); const { name, size = contextSize !== null && contextSize !== void 0 ? contextSize : 32, shape = contextShape !== null && contextShape !== void 0 ? contextShape : 'circular', active = 'unset', activeAppearance = 'ring', idForColor } = props; let { color = 'neutral' } = props; // Resolve 'colorful' to a specific color name if (color === 'colorful') { var _ref; color = avatarColors[getHashCode((_ref = idForColor !== null && idForColor !== void 0 ? idForColor : name) !== null && _ref !== void 0 ? _ref : '') % avatarColors.length]; } const baseId = useId('avatar-'); const root = slot.always(getIntrinsicElementProps('span', { role: 'img', id: baseId, // aria-label and/or aria-labelledby are resolved below ...props, ref }, /* excludedPropNames: */ [ 'name' ]), { elementType: 'span' }); const [imageHidden, setImageHidden] = React.useState(undefined); let image = slot.optional(props.image, { defaultProps: { alt: '', role: 'presentation', 'aria-hidden': true, hidden: imageHidden }, elementType: 'img' }); // Image shouldn't be rendered if its src is not set if (!(image === null || image === void 0 ? void 0 : image.src)) { image = undefined; } // Hide the image if it fails to load and restore it on a successful load if (image) { image.onError = mergeCallbacks(image.onError, ()=>setImageHidden(true)); image.onLoad = mergeCallbacks(image.onLoad, ()=>setImageHidden(undefined)); } // Resolve the initials slot, defaulted to getInitials. let initials = slot.optional(props.initials, { renderByDefault: true, defaultProps: { children: getInitials(name, dir === 'rtl', { firstInitialOnly: size <= 16 }), id: baseId + '__initials' }, elementType: 'span' }); // Don't render the initials slot if it's empty if (!(initials === null || initials === void 0 ? void 0 : initials.children)) { initials = undefined; } // Render the icon slot *only if* there aren't any initials or image to display let icon = undefined; if (!initials && (!image || imageHidden)) { icon = slot.optional(props.icon, { renderByDefault: true, defaultProps: { children: /*#__PURE__*/ React.createElement(PersonRegular, null), 'aria-hidden': true }, elementType: 'span' }); } const badge = slot.optional(props.badge, { defaultProps: { size: getBadgeSize(size), id: baseId + '__badge' }, elementType: PresenceBadge }); let activeAriaLabelElement; // Resolve aria-label and/or aria-labelledby if not provided by the user if (!root['aria-label'] && !root['aria-labelledby']) { if (name) { root['aria-label'] = name; // Include the badge in labelledby if it exists if (badge) { root['aria-labelledby'] = root.id + ' ' + badge.id; } } else if (initials) { // root's aria-label should be the name, but fall back to being labelledby the initials if name is missing root['aria-labelledby'] = initials.id + (badge ? ' ' + badge.id : ''); } // Add the active state to the aria label if (active === 'active' || active === 'inactive') { const activeText = DEFAULT_STRINGS[active]; if (root['aria-labelledby']) { // If using aria-labelledby, render a hidden span and append it to the labelledby const activeId = baseId + '__active'; root['aria-labelledby'] += ' ' + activeId; activeAriaLabelElement = /*#__PURE__*/ React.createElement("span", { hidden: true, id: activeId }, activeText); } else if (root['aria-label']) { // Otherwise, just append it to the aria-label root['aria-label'] += ' ' + activeText; } } } return { size, shape, active, activeAppearance, activeAriaLabelElement, color, components: { root: 'span', initials: 'span', icon: 'span', image: 'img', badge: PresenceBadge }, root, initials, icon, image, badge }; }; const getBadgeSize = (size)=>{ if (size >= 96) { return 'extra-large'; } else if (size >= 64) { return 'large'; } else if (size >= 56) { return 'medium'; } else if (size >= 40) { return 'small'; } else if (size >= 28) { return 'extra-small'; } else { return 'tiny'; } }; const avatarColors = [ 'dark-red', 'cranberry', 'red', 'pumpkin', 'peach', 'marigold', 'gold', 'brass', 'brown', 'forest', 'seafoam', 'dark-green', 'light-teal', 'teal', 'steel', 'blue', 'royal-blue', 'cornflower', 'navy', 'lavender', 'purple', 'grape', 'lilac', 'pink', 'magenta', 'plum', 'beige', 'mink', 'platinum', 'anchor' ]; const getHashCode = (str)=>{ let hashCode = 0; for(let len = str.length - 1; len >= 0; len--){ const ch = str.charCodeAt(len); const shift = len % 8; hashCode ^= (ch << shift) + (ch >> 8 - shift); // eslint-disable-line no-bitwise } return hashCode; };