import { tuple, VueNode } from '../_util/type'; import { CSSProperties, defineComponent, ExtractPropTypes, inject, nextTick, onMounted, onUpdated, PropType, ref, watch, } from 'vue'; import { defaultConfigProvider } from '../config-provider'; import { getPropsSlot } from '../_util/props-util'; import PropTypes from '../_util/vue-types'; const avatarProps = { prefixCls: PropTypes.string, shape: PropTypes.oneOf(tuple('circle', 'square')), size: { type: [Number, String] as PropType<'large' | 'small' | 'default' | number>, default: 'default', }, src: PropTypes.string, /** Srcset of image avatar */ srcset: PropTypes.string, icon: PropTypes.VNodeChild, alt: PropTypes.string, gap: Number, loadError: { type: Function as PropType<() => boolean>, }, }; export type AvatarProps = Partial>; const Avatar = defineComponent({ name: 'AAvatar', props: avatarProps, setup(props, { slots }) { const isImgExist = ref(true); const isMounted = ref(false); const scale = ref(1); const avatarChildrenRef = ref(null); const avatarNodeRef = ref(null); const configProvider = inject('configProvider', defaultConfigProvider); const setScale = () => { if (!avatarChildrenRef.value || !avatarNodeRef.value) { return; } const childrenWidth = avatarChildrenRef.value.offsetWidth; // offsetWidth avoid affecting be transform scale const nodeWidth = avatarNodeRef.value.offsetWidth; // denominator is 0 is no meaning if (childrenWidth !== 0 && nodeWidth !== 0) { const { gap = 4 } = props; if (gap * 2 < nodeWidth) { scale.value = nodeWidth - gap * 2 < childrenWidth ? (nodeWidth - gap * 2) / childrenWidth : 1; } } }; const handleImgLoadError = () => { const { loadError } = props; const errorFlag = loadError?.(); if (errorFlag !== false) { isImgExist.value = false; } }; watch( () => props.src, () => { nextTick(() => { isImgExist.value = true; scale.value = 1; }); }, ); onMounted(() => { nextTick(() => { setScale(); isMounted.value = true; }); }); onUpdated(() => { nextTick(() => { setScale(); }); }); return () => { const { prefixCls: customizePrefixCls, shape, size, src, alt, srcset } = props; const icon = getPropsSlot(slots, props, 'icon'); const getPrefixCls = configProvider.getPrefixCls; const prefixCls = getPrefixCls('avatar', customizePrefixCls); const classString = { [prefixCls]: true, [`${prefixCls}-lg`]: size === 'large', [`${prefixCls}-sm`]: size === 'small', [`${prefixCls}-${shape}`]: shape, [`${prefixCls}-image`]: src && isImgExist.value, [`${prefixCls}-icon`]: icon, }; const sizeStyle: CSSProperties = typeof size === 'number' ? { width: `${size}px`, height: `${size}px`, lineHeight: `${size}px`, fontSize: icon ? `${size / 2}px` : '18px', } : {}; let children: VueNode = slots.default?.(); if (src && isImgExist.value) { children = {alt}; } else if (icon) { children = icon; } else { const childrenNode = avatarChildrenRef.value; if (childrenNode || scale.value !== 1) { const transformString = `scale(${scale.value}) translateX(-50%)`; const childrenStyle: CSSProperties = { msTransform: transformString, WebkitTransform: transformString, transform: transformString, }; const sizeChildrenStyle = typeof size === 'number' ? { lineHeight: `${size}px`, } : {}; children = ( {children} ); } else { children = ( {children} ); } } return ( {children} ); }; }, }); export default Avatar;