feat(Avatar): group add shape & synch demo (#6822)

This commit is contained in:
selicens 2023-08-18 14:22:07 +08:00 committed by GitHub
parent 38dd977b4c
commit cfd4aadc29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 237 additions and 157 deletions

View File

@ -11,7 +11,7 @@ import useConfigInject from '../config-provider/hooks/useConfigInject';
import ResizeObserver from '../vc-resize-observer';
import eagerComputed from '../_util/eagerComputed';
import useStyle from './style';
import { useInjectSize } from './SizeContext';
import { useAvatarInjectContext } from './AvatarContext';
export type AvatarSize = 'large' | 'small' | 'default' | number | ScreenSizeMap;
@ -56,9 +56,9 @@ const Avatar = defineComponent({
const { prefixCls } = useConfigInject('avatar', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const groupSize = useInjectSize();
const avatarCtx = useAvatarInjectContext();
const size = computed(() => {
return props.size === 'default' ? groupSize.value : props.size;
return props.size === 'default' ? avatarCtx.size : props.size;
});
const screens = useBreakpoint();
const responsiveSize = eagerComputed(() => {
@ -135,6 +135,7 @@ const Avatar = defineComponent({
return () => {
const { shape, src, alt, srcset, draggable, crossOrigin } = props;
const mergeShape = avatarCtx.shape ?? shape;
const icon = getPropsSlot(slots, props, 'icon');
const pre = prefixCls.value;
const classString = {
@ -142,7 +143,7 @@ const Avatar = defineComponent({
[pre]: true,
[`${pre}-lg`]: size.value === 'large',
[`${pre}-sm`]: size.value === 'small',
[`${pre}-${shape}`]: shape,
[`${pre}-${mergeShape}`]: true,
[`${pre}-image`]: src && isImgExist.value,
[`${pre}-icon`]: icon,
[hashId.value]: true,

View File

@ -0,0 +1,16 @@
import type { InjectionKey } from 'vue';
import { inject, provide } from 'vue';
import type { ScreenSizeMap } from '../_util/responsiveObserve';
export type AvatarSize = 'large' | 'small' | 'default' | number | ScreenSizeMap;
export interface AvatarContextType {
size?: AvatarSize;
shape?: 'circle' | 'square';
}
const AvatarContextKey: InjectionKey<AvatarContextType> = Symbol('AvatarContextKey');
export const useAvatarInjectContext = () => {
return inject(AvatarContextKey, {});
};
export const useAvatarProviderContext = (context: AvatarContextType) => {
return provide(AvatarContextKey, context);
};

View File

@ -3,11 +3,11 @@ import type { AvatarSize } from './Avatar';
import Avatar from './Avatar';
import Popover from '../popover';
import type { PropType, ExtractPropTypes, CSSProperties } from 'vue';
import { computed, defineComponent } from 'vue';
import { computed, defineComponent, watchEffect } from 'vue';
import { flattenChildren, getPropsSlot } from '../_util/props-util';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import useStyle from './style';
import { useProviderSize } from './SizeContext';
import { useAvatarProviderContext } from './AvatarContext';
export const groupProps = () => ({
prefixCls: String,
@ -23,6 +23,7 @@ export const groupProps = () => ({
type: [Number, String, Object] as PropType<AvatarSize>,
default: 'default' as AvatarSize,
},
shape: { type: String as PropType<'circle' | 'square'>, default: 'circle' },
});
export type AvatarGroupProps = Partial<ExtractPropTypes<ReturnType<typeof groupProps>>>;
@ -36,13 +37,17 @@ const Group = defineComponent({
const { prefixCls, direction } = useConfigInject('avatar', props);
const groupPrefixCls = computed(() => `${prefixCls.value}-group`);
const [wrapSSR, hashId] = useStyle(prefixCls);
useProviderSize(computed(() => props.size));
watchEffect(() => {
const context = { size: props.size, shape: props.shape };
useAvatarProviderContext(context);
});
return () => {
const {
maxPopoverPlacement = 'top',
maxCount,
maxStyle,
maxPopoverTrigger = 'hover',
shape,
} = props;
const cls = {
@ -72,7 +77,7 @@ const Group = defineComponent({
placement={maxPopoverPlacement}
overlayClassName={`${groupPrefixCls.value}-popover`}
>
<Avatar style={maxStyle}>{`+${numOfChildren - maxCount}`}</Avatar>
<Avatar style={maxStyle} shape={shape}>{`+${numOfChildren - maxCount}`}</Avatar>
</Popover>,
);
return wrapSSR(

View File

@ -1,17 +0,0 @@
import type { InjectionKey, Ref } from 'vue';
import { computed, inject, ref, provide } from 'vue';
import type { ScreenSizeMap } from '../_util/responsiveObserve';
export type AvatarSize = 'large' | 'small' | 'default' | number | ScreenSizeMap;
const SizeContextKey: InjectionKey<Ref<AvatarSize>> = Symbol('SizeContextKey');
export const useInjectSize = () => {
return inject(SizeContextKey, ref('default' as AvatarSize));
};
export const useProviderSize = (size: Ref<AvatarSize>) => {
const parentSize = useInjectSize();
provide(
SizeContextKey,
computed(() => size.value || parentSize.value),
);
return size;
};

View File

@ -16,29 +16,20 @@ Usually used for reminders and notifications.
</docs>
<template>
<span style="margin-right: 24px">
<a-space :size="24">
<a-badge :count="1">
<a-avatar shape="square">
<template #icon><UserOutlined /></template>
</a-avatar>
</a-badge>
</span>
<span>
<a-badge dot>
<a-avatar shape="square">
<template #icon><UserOutlined /></template>
</a-avatar>
</a-badge>
</span>
</a-space>
</template>
<script lang="ts" setup>
import { UserOutlined } from '@ant-design/icons-vue';
</script>
<style scoped>
#components-avatar-demo-badge .ant-avatar {
margin-top: 0;
margin-right: 0;
}
</style>

View File

@ -16,31 +16,36 @@ Three sizes and two shapes are available.
</docs>
<template>
<a-avatar :size="64">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar size="large">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar>
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar size="small">
<template #icon><UserOutlined /></template>
</a-avatar>
<br />
<a-avatar shape="square" :size="64">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar shape="square" size="large">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar shape="square">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar shape="square" size="small">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-space direction="vertical" :size="32">
<a-space wrap :size="16">
<a-avatar :size="64">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar size="large">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar>
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar size="small">
<template #icon><UserOutlined /></template>
</a-avatar>
</a-space>
<a-space wrap :size="16">
<a-avatar shape="square" :size="64">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar shape="square" size="large">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar shape="square">
<template #icon><UserOutlined /></template>
</a-avatar>
<a-avatar shape="square" size="small">
<template #icon><UserOutlined /></template>
</a-avatar>
</a-space>
</a-space>
</template>
<script lang="ts" setup>

View File

@ -8,27 +8,22 @@ title:
## zh-CN
对于字符型的头像当字符串较长时字体大小可以根据头像宽度自动调整
对于字符型的头像当字符串较长时字体大小可以根据头像宽度自动调整也可使用 `gap`` 来设置字符距离左右两侧边界单位像素。
## en-US
For letter type Avatar, when the letters are too long to display, the font size can be automatically adjusted according to the width of the Avatar.
For letter type Avatar, when the letters are too long to display, the font size can be automatically adjusted according to the width of the Avatar. You can also use `gap` to set the unit distance between left and right sides.
</docs>
<template>
<a-avatar
shape="square"
size="large"
:style="{ backgroundColor: color, verticalAlign: 'middle' }"
>
<a-avatar size="large" :style="{ backgroundColor: color, verticalAlign: 'middle' }" :gap="gap">
{{ avatarValue }}
</a-avatar>
<a-button
size="small"
:style="{ marginLeft: '16px', verticalAlign: 'middle' }"
@click="changeValue"
>
改变
<a-button size="small" :style="{ margin: '0 16px', verticalAlign: 'middle' }" @click="changeUser">
ChangeUser
</a-button>
<a-button size="small" :style="{ verticalAlign: 'middle' }" @click="changeGap">
ChangeGap
</a-button>
</template>
@ -39,9 +34,16 @@ const UserList = ['U', 'Lucy', 'Tom', 'Edward'];
const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
const avatarValue = ref(UserList[0]);
const color = ref(colorList[0]);
const changeValue = () => {
const changeUser = () => {
const index = UserList.indexOf(avatarValue.value);
avatarValue.value = index < UserList.length - 1 ? UserList[index + 1] : UserList[0];
color.value = index < colorList.length - 1 ? colorList[index + 1] : colorList[0];
};
const GapList = [4, 3, 2, 1];
const gap = ref(GapList[0]);
const changeGap = () => {
const index = GapList.indexOf(gap.value);
gap.value = index < GapList.length - 1 ? GapList[index + 1] : GapList[0];
};
</script>

View File

@ -17,20 +17,22 @@ Avatar group display.
<template>
<a-avatar-group>
<a-avatar src="https://joeschmoe.io/api/v1/random" />
<a-avatar style="background-color: #f56a00">K</a-avatar>
<a-avatar src="https://xsgames.co/randomusers/avatar.php?g=pixel&key=1" />
<a href="https://www.antdv.com">
<a-avatar style="background-color: #f56a00">K</a-avatar>
</a>
<a-tooltip title="Ant User" placement="top">
<a-avatar style="background-color: #87d068">
<template #icon><UserOutlined /></template>
</a-avatar>
</a-tooltip>
<a-avatar style="background-color: #1890ff">
<template #icon><UserOutlined /></template>
<template #icon><AntDesignOutlined /></template>
</a-avatar>
</a-avatar-group>
<a-divider />
<a-avatar-group :max-count="2" :max-style="{ color: '#f56a00', backgroundColor: '#fde3cf' }">
<a-avatar src="https://joeschmoe.io/api/v1/random" />
<a-avatar src="https://xsgames.co/randomusers/avatar.php?g=pixel&key=2" />
<a-avatar style="background-color: #1890ff">K</a-avatar>
<a-tooltip title="Ant User" placement="top">
<a-avatar style="background-color: #87d068">
@ -38,7 +40,7 @@ Avatar group display.
</a-avatar>
</a-tooltip>
<a-avatar style="background-color: #1890ff">
<template #icon><UserOutlined /></template>
<template #icon><AntDesignOutlined /></template>
</a-avatar>
</a-avatar-group>
<a-divider />
@ -50,7 +52,7 @@ Avatar group display.
backgroundColor: '#fde3cf',
}"
>
<a-avatar src="https://joeschmoe.io/api/v1/random" />
<a-avatar src="https://xsgames.co/randomusers/avatar.php?g=pixel&key=3" />
<a-avatar style="background-color: #1890ff">K</a-avatar>
<a-tooltip title="Ant User" placement="top">
<a-avatar style="background-color: #87d068">
@ -58,11 +60,42 @@ Avatar group display.
</a-avatar>
</a-tooltip>
<a-avatar style="background-color: #1890ff">
<template #icon><UserOutlined /></template>
<template #icon><AntDesignOutlined /></template>
</a-avatar>
</a-avatar-group>
<a-divider />
<a-avatar-group
:max-count="2"
max-popover-trigger="click"
size="large"
:max-style="{ color: '#f56a00', backgroundColor: '#fde3cf', cursor: 'pointer' }"
>
<a-avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
<a-avatar style="background-color: #f56a00">K</a-avatar>
<a-tooltip title="Ant User" placement="top">
<a-avatar style="background-color: #87d068">
<template #icon><UserOutlined /></template>
</a-avatar>
</a-tooltip>
<a-avatar style="background-color: #1890ff">
<template #icon><AntDesignOutlined /></template>
</a-avatar>
</a-avatar-group>
<a-divider />
<a-avatar-group shape="square">
<a-avatar style="background-color: #fde3cf">A</a-avatar>
<a-avatar style="background-color: #f56a00">K</a-avatar>
<a-tooltip title="Ant User" placement="top">
<a-avatar style="background-color: #87d068">
<template #icon><UserOutlined /></template>
</a-avatar>
</a-tooltip>
<a-avatar style="background-color: #1890ff">
<template #icon><AntDesignOutlined /></template>
</a-avatar>
</a-avatar-group>
</template>
<script lang="ts" setup>
import { UserOutlined } from '@ant-design/icons-vue';
import { UserOutlined, AntDesignOutlined } from '@ant-design/icons-vue';
</script>

View File

@ -36,10 +36,3 @@ export default defineComponent({
},
});
</script>
<style>
[id^='components-avatar-demo-'] .ant-avatar {
margin-top: 16px;
margin-right: 16px;
}
</style>

View File

@ -16,20 +16,22 @@ Image, Icon and letter are supported, and the latter two kinds avatar can have c
</docs>
<template>
<a-avatar>
<template #icon>
<UserOutlined />
</template>
</a-avatar>
<a-avatar>U</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">
<template #icon>
<UserOutlined />
</template>
</a-avatar>
<a-space :size="16" wrap>
<a-avatar>
<template #icon>
<UserOutlined />
</template>
</a-avatar>
<a-avatar>U</a-avatar>
<a-avatar :size="40">USER</a-avatar>
<a-avatar src="https://www.antdv.com/assets/logo.1ef800a8.svg" />
<a-avatar style="color: #f56a00; background-color: #fde3cf">U</a-avatar>
<a-avatar style="background-color: #87d068">
<template #icon>
<UserOutlined />
</template>
</a-avatar>
</a-space>
</template>
<script lang="ts" setup>

View File

@ -34,3 +34,4 @@ Avatars can be used to represent people or objects. It supports images, `Icon`s,
| maxPopoverTrigger | Set the trigger of excess avatar Popover | `hover` \| `focus` \| `click` | `hover` | 3.0 |
| maxStyle | The style of excess avatar style | CSSProperties | - | |
| size | The size of the avatar | number \| `large` \| `small` \| `default` \| { xs: number, sm: number, ...} | `default` | |
| shape | The shape of the avatar | `circle` \| `square` | `circle` | 4.0 |

View File

@ -39,3 +39,4 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*YbgyQaRGz-UAAA
| maxPopoverTrigger | 设置多余头像 Popover 的触发方式 | `hover` \| `focus` \| `click` | `hover` | 3.0 |
| maxStyle | 多余头像样式 | CSSProperties | - | |
| size | 设置头像的大小 | number \| `large` \| `small` \| `default` \| { xs: number, sm: number, ...} | `default` | |
| shape | 设置头像的形状 | `circle` \| `square` | `circle` | 4.0 |

View File

@ -3,20 +3,57 @@ import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../style';
export interface ComponentToken {}
export interface ComponentToken {
/**
* @desc
* @descEN Background color of Avatar
*/
containerSize: number;
/**
* @desc
* @descEN Size of large Avatar
*/
containerSizeLG: number;
/**
* @desc
* @descEN Size of small Avatar
*/
containerSizeSM: number;
/**
* @desc
* @descEN Font size of Avatar
*/
textFontSize: number;
/**
* @desc
* @descEN Font size of large Avatar
*/
textFontSizeLG: number;
/**
* @desc
* @descEN Font size of small Avatar
*/
textFontSizeSM: number;
/**
* @desc
* @descEN Spacing between avatars in a group
*/
groupSpace: number;
/**
* @desc
* @descEN Overlapping of avatars in a group
*/
groupOverlapping: number;
/**
* @desc
* @descEN Border color of avatars in a group
*/
groupBorderColor: string;
}
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;
};
@ -27,12 +64,12 @@ const genBaseStyle: GenerateStyle<AvatarToken> = token => {
iconCls,
avatarBg,
avatarColor,
avatarSizeBase,
avatarSizeLG,
avatarSizeSM,
avatarFontSizeBase,
avatarFontSizeLG,
avatarFontSizeSM,
containerSize,
containerSizeLG,
containerSizeSM,
textFontSize,
textFontSizeLG,
textFontSizeSM,
borderRadius,
borderRadiusLG,
borderRadiusSM,
@ -89,14 +126,14 @@ const genBaseStyle: GenerateStyle<AvatarToken> = token => {
display: 'block',
},
...avatarSizeStyle(avatarSizeBase, avatarFontSizeBase, borderRadius),
...avatarSizeStyle(containerSize, textFontSize, borderRadius),
[`&-lg`]: {
...avatarSizeStyle(avatarSizeLG, avatarFontSizeLG, borderRadiusLG),
...avatarSizeStyle(containerSizeLG, textFontSizeLG, borderRadiusLG),
},
[`&-sm`]: {
...avatarSizeStyle(avatarSizeSM, avatarFontSizeSM, borderRadiusSM),
...avatarSizeStyle(containerSizeSM, textFontSizeSM, borderRadiusSM),
},
'> img': {
@ -110,55 +147,65 @@ const genBaseStyle: GenerateStyle<AvatarToken> = token => {
};
const genGroupStyle: GenerateStyle<AvatarToken> = token => {
const { componentCls, avatarGroupBorderColor, avatarGroupSpace } = token;
const { componentCls, groupBorderColor, groupOverlapping, groupSpace } = token;
return {
[`${componentCls}-group`]: {
display: 'inline-flex',
[`${componentCls}`]: {
borderColor: avatarGroupBorderColor,
borderColor: groupBorderColor,
},
[`> *:not(:first-child)`]: {
marginInlineStart: avatarGroupSpace,
marginInlineStart: groupOverlapping,
},
},
[`${componentCls}-group-popover`]: {
[`${componentCls} + ${componentCls}`]: {
marginInlineStart: groupSpace,
},
},
};
};
export default genComponentStyleHook('Avatar', token => {
const {
colorTextLightSolid,
export default genComponentStyleHook(
'Avatar',
token => {
const { colorTextLightSolid, colorTextPlaceholder } = token;
const avatarToken = mergeToken<AvatarToken>(token, {
avatarBg: colorTextPlaceholder,
avatarColor: colorTextLightSolid,
});
return [genBaseStyle(avatarToken), genGroupStyle(avatarToken)];
},
token => {
const {
controlHeight,
controlHeightLG,
controlHeightSM,
controlHeight,
controlHeightLG,
controlHeightSM,
fontSize,
fontSizeLG,
fontSizeXL,
fontSizeHeading3,
fontSize,
fontSizeLG,
fontSizeXL,
fontSizeHeading3,
marginXS,
marginXXS,
colorBorderBg,
} = token;
return {
containerSize: controlHeight,
containerSizeLG: controlHeightLG,
containerSizeSM: controlHeightSM,
marginXS,
colorBorderBg,
colorTextPlaceholder,
} = token;
textFontSize: Math.round((fontSizeLG + fontSizeXL) / 2),
textFontSizeLG: fontSizeHeading3,
textFontSizeSM: fontSize,
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)];
});
groupSpace: marginXXS,
groupOverlapping: -marginXS,
groupBorderColor: colorBorderBg,
};
},
);