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

View File

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

View File

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