UNPKG

@nex-ui/react

Version:

🎉 A beautiful, modern, and reliable React component library.

169 lines (166 loc) • 4.67 kB
"use client"; import { jsx } from 'react/jsx-runtime'; import { useMemo, useState, useEffect } from 'react'; import { useAvatarGroup } from './AvatarGroupContext.mjs'; import { useDefaultProps } from '../utils/useDefaultProps.mjs'; import { useStyles } from '../utils/useStyles.mjs'; import { useSlot } from '../utils/useSlot.mjs'; import { useNexUI } from '../provider/Context.mjs'; import { composeClasses } from '../utils/composeClasses.mjs'; import { avatarRecipe } from '../../theme/recipes/avatar.mjs'; import { getUtilityClass } from '../utils/getUtilityClass.mjs'; const useSlotClasses = (ownerState)=>{ const { prefix } = useNexUI(); const { color, size, radius, classes, outlined } = ownerState; return useMemo(()=>{ const avatarRoot = `${prefix}-avatar`; const slots = { root: [ 'root', `radius-${radius}`, `size-${size}`, `color-${color}`, outlined && 'outlined' ], img: [ `img` ] }; return composeClasses(slots, getUtilityClass(avatarRoot), classes); }, [ classes, color, outlined, prefix, radius, size ]); }; const useLoaded = ({ src, srcSet })=>{ const [loaded, setLoaded] = useState(false); useEffect(()=>{ if (!src && !srcSet) { return; } setLoaded(false); let active = true; const image = new Image(); image.onload = ()=>{ if (!active) { return; } setLoaded('loaded'); }; image.onerror = ()=>{ if (!active) { return; } setLoaded('error'); }; image.src = src; if (srcSet) { image.srcset = srcSet; } return ()=>{ active = false; }; }, [ src, srcSet ]); return loaded; }; const useSlotAriaProps = (ownerState)=>{ const { alt, children, loaded, 'aria-label': ariaLabel, role = 'img' } = ownerState; return useMemo(()=>{ let root = {}; if (loaded === false || loaded === 'error') { if (typeof children === 'string') { root = { role, 'aria-label': ariaLabel ?? children }; } else if (typeof alt === 'string') { root = { role, 'aria-label': ariaLabel ?? alt }; } } return { root }; }, [ alt, ariaLabel, children, loaded, role ]); }; const Avatar = (inProps)=>{ const props = useDefaultProps({ name: 'Avatar', props: inProps }); const groupCtx = useAvatarGroup(); const inGroup = !!groupCtx; const { src, alt, srcSet, slotProps, children: childrenProp, size = groupCtx?.size ?? 'md', radius = groupCtx?.radius ?? size, color = groupCtx?.color ?? 'gray', outlined = groupCtx?.outlined ?? false, ...remainingProps } = props; const loaded = useLoaded({ src, srcSet }); const ownerState = { ...props, size, radius, color, outlined, inGroup, loaded }; const styles = useStyles({ ownerState, name: 'Avatar', recipe: avatarRecipe }); const classes = useSlotClasses(ownerState); const slotAriaProps = useSlotAriaProps(ownerState); const [AvatarRoot, getAvatarRootProps] = useSlot({ ownerState, elementType: 'div', externalForwardedProps: remainingProps, classNames: classes.root, style: styles.root, a11y: slotAriaProps.root }); const [AvatarImg, getAvatarImgProps] = useSlot({ ownerState, elementType: 'img', externalSlotProps: slotProps?.img, classNames: classes.img, style: styles.img, additionalProps: { src, alt, srcSet } }); let children = null; const hasImg = src || srcSet; if (hasImg && loaded === 'loaded') { children = /*#__PURE__*/ jsx(AvatarImg, { ...getAvatarImgProps() }); } else if (childrenProp) { children = childrenProp; } else if (hasImg && alt) { children = alt[0]; } return /*#__PURE__*/ jsx(AvatarRoot, { ...getAvatarRootProps(), children: children }); }; Avatar.displayName = 'Avatar'; export { Avatar };