refactor: avatar

pull/6213/head
tangjinzhou 2023-01-25 16:39:56 +08:00
parent 641714734a
commit f8ddc430cf
10 changed files with 248 additions and 182 deletions

View File

@ -11,6 +11,7 @@ import useConfigInject from '../_util/hooks/useConfigInject';
import ResizeObserver from '../vc-resize-observer'; import ResizeObserver from '../vc-resize-observer';
import { useInjectSize } from '../_util/hooks/useSize'; import { useInjectSize } from '../_util/hooks/useSize';
import eagerComputed from '../_util/eagerComputed'; import eagerComputed from '../_util/eagerComputed';
import useStyle from './style';
export type AvatarSize = 'large' | 'small' | 'default' | number | ScreenSizeMap; export type AvatarSize = 'large' | 'small' | 'default' | number | ScreenSizeMap;
@ -51,7 +52,7 @@ const Avatar = defineComponent({
const avatarNodeRef = ref<HTMLElement>(null); const avatarNodeRef = ref<HTMLElement>(null);
const { prefixCls } = useConfigInject('avatar', props); const { prefixCls } = useConfigInject('avatar', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const groupSize = useInjectSize(); const groupSize = useInjectSize();
const size = computed(() => { const size = computed(() => {
return props.size === 'default' ? groupSize.value : props.size; return props.size === 'default' ? groupSize.value : props.size;
@ -141,6 +142,7 @@ const Avatar = defineComponent({
[`${pre}-${shape}`]: shape, [`${pre}-${shape}`]: shape,
[`${pre}-image`]: src && isImgExist.value, [`${pre}-image`]: src && isImgExist.value,
[`${pre}-icon`]: icon, [`${pre}-icon`]: icon,
[hashId.value]: true,
}; };
const sizeStyle: CSSProperties = const sizeStyle: CSSProperties =
@ -199,7 +201,7 @@ const Avatar = defineComponent({
</span> </span>
); );
} }
return ( return wrapSSR(
<span <span
{...attrs} {...attrs}
ref={avatarNodeRef} ref={avatarNodeRef}
@ -207,7 +209,7 @@ const Avatar = defineComponent({
style={[sizeStyle, responsiveSizeStyle(!!icon), attrs.style as CSSProperties]} style={[sizeStyle, responsiveSizeStyle(!!icon), attrs.style as CSSProperties]}
> >
{childrenToRender} {childrenToRender}
</span> </span>,
); );
}; };
}, },

View File

@ -3,10 +3,11 @@ import type { AvatarSize } from './Avatar';
import Avatar from './Avatar'; import Avatar from './Avatar';
import Popover from '../popover'; import Popover from '../popover';
import type { PropType, ExtractPropTypes, CSSProperties } from 'vue'; import type { PropType, ExtractPropTypes, CSSProperties } from 'vue';
import { defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import { flattenChildren, getPropsSlot } from '../_util/props-util'; import { flattenChildren, getPropsSlot } from '../_util/props-util';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import useProvideSize from '../_util/hooks/useSize'; import useProvideSize from '../_util/hooks/useSize';
import useStyle from './style';
export const groupProps = () => ({ export const groupProps = () => ({
prefixCls: String, prefixCls: String,
@ -32,7 +33,9 @@ const Group = defineComponent({
inheritAttrs: false, inheritAttrs: false,
props: groupProps(), props: groupProps(),
setup(props, { slots, attrs }) { setup(props, { slots, attrs }) {
const { prefixCls, direction } = useConfigInject('avatar-group', props); const { prefixCls, direction } = useConfigInject('avatar', props);
const groupPrefixCls = computed(() => `${prefixCls.value}-group`);
const [wrapSSR, hashId] = useStyle(prefixCls);
useProvideSize<AvatarSize>(props); useProvideSize<AvatarSize>(props);
return () => { return () => {
const { const {
@ -43,9 +46,10 @@ const Group = defineComponent({
} = props; } = props;
const cls = { const cls = {
[prefixCls.value]: true, [groupPrefixCls.value]: true,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl', [`${groupPrefixCls.value}-rtl`]: direction.value === 'rtl',
[`${attrs.class}`]: !!attrs.class, [`${attrs.class}`]: !!attrs.class,
[hashId.value]: true,
}; };
const children = getPropsSlot(slots, props); const children = getPropsSlot(slots, props);
@ -66,22 +70,22 @@ const Group = defineComponent({
content={childrenHidden} content={childrenHidden}
trigger={maxPopoverTrigger} trigger={maxPopoverTrigger}
placement={maxPopoverPlacement} placement={maxPopoverPlacement}
overlayClassName={`${prefixCls.value}-popover`} overlayClassName={`${groupPrefixCls.value}-popover`}
> >
<Avatar style={maxStyle}>{`+${numOfChildren - maxCount}`}</Avatar> <Avatar style={maxStyle}>{`+${numOfChildren - maxCount}`}</Avatar>
</Popover>, </Popover>,
); );
return ( return wrapSSR(
<div {...attrs} class={cls} style={attrs.style as CSSProperties}> <div {...attrs} class={cls} style={attrs.style as CSSProperties}>
{childrenShow} {childrenShow}
</div> </div>,
); );
} }
return ( return wrapSSR(
<div {...attrs} class={cls} style={attrs.style as CSSProperties}> <div {...attrs} class={cls} style={attrs.style as CSSProperties}>
{childrenWithProps} {childrenWithProps}
</div> </div>,
); );
}; };
}, },

View File

@ -22,7 +22,7 @@ Image, Icon and letter are supported, and the latter two kinds avatar can have c
</template> </template>
</a-avatar> </a-avatar>
<a-avatar>U</a-avatar> <a-avatar>U</a-avatar>
<a-avatar>USER</a-avatar> <a-avatar :size="40">USER</a-avatar>
<a-avatar src="https://joeschmoe.io/api/v1/random" /> <a-avatar src="https://joeschmoe.io/api/v1/random" />
<a-avatar style="color: #f56a00; background-color: #fde3cf">U</a-avatar> <a-avatar style="color: #f56a00; background-color: #fde3cf">U</a-avatar>
<a-avatar style="background-color: #87d068"> <a-avatar style="background-color: #87d068">

View File

@ -1,17 +0,0 @@
.@{avatar-prefix-cls}-group {
display: inline-flex;
.@{avatar-prefix-cls} {
border: 1px solid @avatar-group-border-color;
&:not(:first-child) {
margin-left: @avatar-group-overlapping;
}
}
&-popover {
.@{ant-prefix}-avatar + .@{ant-prefix}-avatar {
margin-left: @avatar-group-space;
}
}
}

View File

@ -1,70 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@avatar-prefix-cls: ~'@{ant-prefix}-avatar';
.@{avatar-prefix-cls} {
.reset-component();
position: relative;
display: inline-block;
overflow: hidden;
color: @avatar-color;
white-space: nowrap;
text-align: center;
vertical-align: middle;
background: @avatar-bg;
&-image {
background: transparent;
}
.@{ant-prefix}-image-img {
display: block;
}
.avatar-size(@avatar-size-base, @avatar-font-size-base);
&-lg {
.avatar-size(@avatar-size-lg, @avatar-font-size-lg);
}
&-sm {
.avatar-size(@avatar-size-sm, @avatar-font-size-sm);
}
&-square {
border-radius: @avatar-border-radius;
}
& > img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
}
.avatar-size(@size, @font-size) {
width: @size;
height: @size;
line-height: @size;
border-radius: 50%;
&-string {
position: absolute;
left: 50%;
transform-origin: 0 center;
}
&.@{avatar-prefix-cls}-icon {
font-size: @font-size;
> .@{iconfont-css-prefix} {
margin: 0;
}
}
}
@import './group';
@import './rtl';

View File

@ -0,0 +1,164 @@
import type { CSSObject } from '../../_util/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../_style';
export interface ComponentToken {}
type AvatarToken = FullToken<'Avatar'> & {
avatarBg: string;
avatarColor: string;
avatarSizeBase: number;
avatarSizeLG: number;
avatarSizeSM: number;
avatarFontSizeBase: number;
avatarFontSizeLG: number;
avatarFontSizeSM: number;
avatarGroupOverlapping: number;
avatarGroupSpace: number;
avatarGroupBorderColor: string;
avatarBgColor: string;
};
const genBaseStyle: GenerateStyle<AvatarToken> = token => {
const {
antCls,
componentCls,
iconCls,
avatarBg,
avatarColor,
avatarSizeBase,
avatarSizeLG,
avatarSizeSM,
avatarFontSizeBase,
avatarFontSizeLG,
avatarFontSizeSM,
borderRadius,
borderRadiusLG,
borderRadiusSM,
lineWidth,
lineType,
} = token;
// Avatar size style
const avatarSizeStyle = (size: number, fontSize: number, radius: number): CSSObject => ({
width: size,
height: size,
lineHeight: `${size - lineWidth * 2}px`,
borderRadius: '50%',
[`&${componentCls}-square`]: {
borderRadius: radius,
},
[`${componentCls}-string`]: {
position: 'absolute',
left: {
_skip_check_: true,
value: '50%',
},
transformOrigin: '0 center',
},
[`&${componentCls}-icon`]: {
fontSize,
[`> ${iconCls}`]: {
margin: 0,
},
},
});
return {
[componentCls]: {
...resetComponent(token),
position: 'relative',
display: 'inline-block',
overflow: 'hidden',
color: avatarColor,
whiteSpace: 'nowrap',
textAlign: 'center',
verticalAlign: 'middle',
background: avatarBg,
border: `${lineWidth}px ${lineType} transparent`,
[`&-image`]: {
background: 'transparent',
},
[`${antCls}-image-img`]: {
display: 'block',
},
...avatarSizeStyle(avatarSizeBase, avatarFontSizeBase, borderRadius),
[`&-lg`]: {
...avatarSizeStyle(avatarSizeLG, avatarFontSizeLG, borderRadiusLG),
},
[`&-sm`]: {
...avatarSizeStyle(avatarSizeSM, avatarFontSizeSM, borderRadiusSM),
},
'> img': {
display: 'block',
width: '100%',
height: '100%',
objectFit: 'cover',
},
},
};
};
const genGroupStyle: GenerateStyle<AvatarToken> = token => {
const { componentCls, avatarGroupBorderColor, avatarGroupSpace } = token;
return {
[`${componentCls}-group`]: {
display: 'inline-flex',
[`${componentCls}`]: {
borderColor: avatarGroupBorderColor,
},
[`> *:not(:first-child)`]: {
marginInlineStart: avatarGroupSpace,
},
},
};
};
export default genComponentStyleHook('Avatar', token => {
const {
colorTextLightSolid,
controlHeight,
controlHeightLG,
controlHeightSM,
fontSize,
fontSizeLG,
fontSizeXL,
fontSizeHeading3,
marginXS,
colorBorderBg,
colorTextPlaceholder,
} = token;
const avatarToken = mergeToken<AvatarToken>(token, {
avatarBg: colorTextPlaceholder,
avatarColor: colorTextLightSolid,
avatarSizeBase: controlHeight,
avatarSizeLG: controlHeightLG,
avatarSizeSM: controlHeightSM,
avatarFontSizeBase: Math.round((fontSizeLG + fontSizeXL) / 2),
avatarFontSizeLG: fontSizeHeading3,
avatarFontSizeSM: fontSize,
avatarGroupSpace: -marginXS,
avatarGroupBorderColor: colorBorderBg,
});
return [genBaseStyle(avatarToken), genGroupStyle(avatarToken)];
});

View File

@ -1,2 +0,0 @@
import '../../style/index.less';
import './index.less';

View File

@ -1,15 +0,0 @@
.@{avatar-prefix-cls}-group {
&-rtl {
.@{avatar-prefix-cls}:not(:first-child) {
margin-right: @avatar-group-overlapping;
margin-left: 0;
}
}
&-popover.@{ant-prefix}-popover-rtl {
.@{ant-prefix}-avatar + .@{ant-prefix}-avatar {
margin-right: @avatar-group-space;
margin-left: 0;
}
}
}

View File

@ -6,7 +6,7 @@ import './grid/style';
import './tag/style'; import './tag/style';
import './rate/style'; import './rate/style';
import './pagination/style'; import './pagination/style';
import './avatar/style'; // import './avatar/style';
import './badge/style'; import './badge/style';
import './tabs/style'; import './tabs/style';
import './input/style'; import './input/style';

View File

@ -1,6 +1,6 @@
import type { ComponentToken as AlertComponentToken } from '../../alert/style'; import type { ComponentToken as AlertComponentToken } from '../../alert/style';
// import type { ComponentToken as AnchorComponentToken } from '../../anchor/style'; // import type { ComponentToken as AnchorComponentToken } from '../../anchor/style';
// import type { ComponentToken as AvatarComponentToken } from '../../avatar/style'; import type { ComponentToken as AvatarComponentToken } from '../../avatar/style';
// import type { ComponentToken as BackTopComponentToken } from '../../back-top/style'; // import type { ComponentToken as BackTopComponentToken } from '../../back-top/style';
// import type { ComponentToken as ButtonComponentToken } from '../../button/style'; // import type { ComponentToken as ButtonComponentToken } from '../../button/style';
// import type { ComponentToken as FloatButtonComponentToken } from '../../float-button/style'; // import type { ComponentToken as FloatButtonComponentToken } from '../../float-button/style';
@ -53,67 +53,67 @@ import type { ComponentToken as AlertComponentToken } from '../../alert/style';
export interface ComponentTokenMap { export interface ComponentTokenMap {
Affix?: {}; Affix?: {};
Alert?: AlertComponentToken; Alert?: AlertComponentToken;
} // Anchor?: AnchorComponentToken;
// Anchor?: AnchorComponentToken; Avatar?: AvatarComponentToken;
// Avatar?: AvatarComponentToken; // BackTop?: BackTopComponentToken;
// BackTop?: BackTopComponentToken; // Badge?: {};
// Badge?: {}; // Button?: ButtonComponentToken;
// Button?: ButtonComponentToken; // Breadcrumb?: {};
// Breadcrumb?: {}; // Card?: CardComponentToken;
// Card?: CardComponentToken; // Carousel?: CarouselComponentToken;
// Carousel?: CarouselComponentToken; // Cascader?: CascaderComponentToken;
// Cascader?: CascaderComponentToken; // Checkbox?: CheckboxComponentToken;
// Checkbox?: CheckboxComponentToken; // Collapse?: CollapseComponentToken;
// Collapse?: CollapseComponentToken; // DatePicker?: DatePickerComponentToken;
// DatePicker?: DatePickerComponentToken; // Descriptions?: {};
// Descriptions?: {}; // Divider?: DividerComponentToken;
// Divider?: DividerComponentToken; // Drawer?: DrawerComponentToken;
// Drawer?: DrawerComponentToken; // Dropdown?: DropdownComponentToken;
// Dropdown?: DropdownComponentToken; // Empty?: EmptyComponentToken;
// Empty?: EmptyComponentToken; // FloatButton?: FloatButtonComponentToken;
// FloatButton?: FloatButtonComponentToken; // Form?: {};
// Form?: {}; // Grid?: {};
// Grid?: {}; // Image?: ImageComponentToken;
// Image?: ImageComponentToken; // Input?: {};
// Input?: {}; // InputNumber?: InputNumberComponentToken;
// InputNumber?: InputNumberComponentToken; // Layout?: LayoutComponentToken;
// Layout?: LayoutComponentToken; // List?: ListComponentToken;
// List?: ListComponentToken; // Mentions?: MentionsComponentToken;
// Mentions?: MentionsComponentToken; // Notification?: NotificationComponentToken;
// Notification?: NotificationComponentToken; // Pagination?: {};
// Pagination?: {}; // Popover?: PopoverComponentToken;
// Popover?: PopoverComponentToken; // Popconfirm?: PopconfirmComponentToken;
// Popconfirm?: PopconfirmComponentToken; // Rate?: RateComponentToken;
// Rate?: RateComponentToken; // Radio?: RadioComponentToken;
// Radio?: RadioComponentToken; // Result?: ResultComponentToken;
// Result?: ResultComponentToken; // Segmented?: SegmentedComponentToken;
// Segmented?: SegmentedComponentToken; // Select?: SelectComponentToken;
// Select?: SelectComponentToken; // Skeleton?: SkeletonComponentToken;
// Skeleton?: SkeletonComponentToken; // Slider?: SliderComponentToken;
// Slider?: SliderComponentToken; // Spin?: SpinComponentToken;
// Spin?: SpinComponentToken; // Statistic?: {};
// Statistic?: {}; // Switch?: {};
// Switch?: {}; // Tag?: TagComponentToken;
// Tag?: TagComponentToken; // Tree?: {};
// Tree?: {}; // TreeSelect?: {};
// TreeSelect?: {}; // Typography?: TypographyComponentToken;
// Typography?: TypographyComponentToken; // Timeline?: TimelineComponentToken;
// Timeline?: TimelineComponentToken; // Transfer?: TransferComponentToken;
// Transfer?: TransferComponentToken; // Tabs?: TabsComponentToken;
// Tabs?: TabsComponentToken; // Calendar?: CalendarComponentToken;
// Calendar?: CalendarComponentToken; // Steps?: StepsComponentToken;
// Steps?: StepsComponentToken; // Menu?: MenuComponentToken;
// Menu?: MenuComponentToken; // Modal?: ModalComponentToken;
// Modal?: ModalComponentToken; // Message?: MessageComponentToken;
// Message?: MessageComponentToken; // Upload?: UploadComponentToken;
// Upload?: UploadComponentToken; // Tooltip?: TooltipComponentToken;
// Tooltip?: TooltipComponentToken; // Table?: TableComponentToken;
// Table?: TableComponentToken; // Space?: SpaceComponentToken;
// Space?: SpaceComponentToken; // Progress?: ProgressComponentToken;
// Progress?: ProgressComponentToken; // Tour?: TourComponentToken;
// Tour?: TourComponentToken; // QRCode?: QRCodeComponentToken;
// QRCode?: QRCodeComponentToken; // App?: AppComponentToken;
// App?: AppComponentToken;
// /** @private Internal TS definition. Do not use. */ // /** @private Internal TS definition. Do not use. */
// Wave?: WaveToken; // Wave?: WaveToken;
}