Browse Source

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

pull/6871/head
selicens 1 year ago committed by GitHub
parent
commit
cfd4aadc29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      components/avatar/Avatar.tsx
  2. 16
      components/avatar/AvatarContext.ts
  3. 13
      components/avatar/Group.tsx
  4. 17
      components/avatar/SizeContext.ts
  5. 13
      components/avatar/demo/badge.vue
  6. 55
      components/avatar/demo/basic.vue
  7. 30
      components/avatar/demo/dynamic.vue
  8. 49
      components/avatar/demo/group.vue
  9. 7
      components/avatar/demo/index.vue
  10. 30
      components/avatar/demo/type.vue
  11. 1
      components/avatar/index.en-US.md
  12. 1
      components/avatar/index.zh-CN.md
  13. 159
      components/avatar/style/index.ts

9
components/avatar/Avatar.tsx

@ -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,

16
components/avatar/AvatarContext.ts

@ -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);
};

13
components/avatar/Group.tsx

@ -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(

17
components/avatar/SizeContext.ts

@ -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;
};

13
components/avatar/demo/badge.vue

@ -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>

55
components/avatar/demo/basic.vue

@ -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>

30
components/avatar/demo/dynamic.vue

@ -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>

49
components/avatar/demo/group.vue

@ -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>

7
components/avatar/demo/index.vue

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

30
components/avatar/demo/type.vue

@ -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>

1
components/avatar/index.en-US.md

@ -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 |

1
components/avatar/index.zh-CN.md

@ -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 |

159
components/avatar/style/index.ts

@ -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,
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,
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,
avatarFontSizeBase: Math.round((fontSizeLG + fontSizeXL) / 2),
avatarFontSizeLG: fontSizeHeading3,
avatarFontSizeSM: fontSize,
avatarGroupSpace: -marginXS,
avatarGroupBorderColor: colorBorderBg,
});
return [genBaseStyle(avatarToken), genGroupStyle(avatarToken)];
});
fontSize,
fontSizeLG,
fontSizeXL,
fontSizeHeading3,
marginXS,
marginXXS,
colorBorderBg,
} = token;
return {
containerSize: controlHeight,
containerSizeLG: controlHeightLG,
containerSizeSM: controlHeightSM,
textFontSize: Math.round((fontSizeLG + fontSizeXL) / 2),
textFontSizeLG: fontSizeHeading3,
textFontSizeSM: fontSize,
groupSpace: marginXXS,
groupOverlapping: -marginXS,
groupBorderColor: colorBorderBg,
};
},
);

Loading…
Cancel
Save