UNPKG

@nex-ui/react

Version:

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

153 lines (150 loc) • 4.07 kB
"use client"; import { jsx } from 'react/jsx-runtime'; import { useState, useEffect, useMemo } from 'react'; import { useAvatarGroup } from './AvatarGroupContext.mjs'; import { useDefaultProps } from '../utils/useDefaultProps.mjs'; import { useStyles } from '../utils/useStyles.mjs'; import { useSlotClasses } from '../utils/useSlotClasses.mjs'; import { useSlot } from '../utils/useSlot.mjs'; import { avatarRecipe } from '../../theme/recipes/avatar.mjs'; const slots = [ 'root', 'img' ]; 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, classNames, 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 slotClasses = useSlotClasses({ name: 'Avatar', slots, classNames }); const slotAriaProps = useSlotAriaProps(ownerState); const [AvatarRoot, getAvatarRootProps] = useSlot({ elementType: 'div', externalForwardedProps: remainingProps, classNames: slotClasses.root, style: styles.root, a11y: slotAriaProps.root, dataAttrs: { radius, size, color, outlined, inGroup } }); const [AvatarImg, getAvatarImgProps] = useSlot({ elementType: 'img', externalSlotProps: slotProps?.img, classNames: slotClasses.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; } return /*#__PURE__*/ jsx(AvatarRoot, { ...getAvatarRootProps(), children: children }); }; Avatar.displayName = 'Avatar'; export { Avatar };