feat: update avatar
parent
7a8bef0ef3
commit
2394d9f844
|
@ -1,5 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
dev: {
|
dev: {
|
||||||
componentName: 'auto-complete', // dev components
|
componentName: 'avatar', // dev components
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumerProps } from '../config-provider';
|
||||||
import Icon from '../icon';
|
import Icon from '../icon';
|
||||||
import { getListeners } from '../_util/props-util';
|
import { getListeners, getComponentFromProp } from '../_util/props-util';
|
||||||
|
import PropTypes from '../_util/vue-types';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AAvatar',
|
name: 'AAvatar',
|
||||||
|
@ -22,7 +23,7 @@ export default {
|
||||||
src: String,
|
src: String,
|
||||||
/** Srcset of image avatar */
|
/** Srcset of image avatar */
|
||||||
srcSet: String,
|
srcSet: String,
|
||||||
icon: String,
|
icon: PropTypes.any,
|
||||||
alt: String,
|
alt: String,
|
||||||
loadError: Function,
|
loadError: Function,
|
||||||
},
|
},
|
||||||
|
@ -32,6 +33,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isImgExist: true,
|
isImgExist: true,
|
||||||
|
isMounted: false,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -46,38 +48,35 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.prevChildren = this.$slots.default;
|
this.$nextTick(() => {
|
||||||
this.prevState = { ...this.$data };
|
this.setScale();
|
||||||
|
this.isMounted = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.setScale();
|
this.setScale();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updated() {
|
|
||||||
if (
|
|
||||||
this.preChildren !== this.$slots.default ||
|
|
||||||
(this.prevState.scale !== this.$data.scale && this.$data.scale === 1) ||
|
|
||||||
this.prevState.isImgExist !== this.$data.isImgExist
|
|
||||||
) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.setScale();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.preChildren = this.$slots.default;
|
|
||||||
this.prevState = { ...this.$data };
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
setScale() {
|
setScale() {
|
||||||
const childrenNode = this.$refs.avatarChildren;
|
if (!this.$refs.avatarChildren || !this.$refs.avatarNode) {
|
||||||
if (childrenNode) {
|
return;
|
||||||
const childrenWidth = childrenNode.offsetWidth;
|
|
||||||
const avatarWidth = this.$el.getBoundingClientRect().width;
|
|
||||||
if (avatarWidth - 8 < childrenWidth) {
|
|
||||||
this.scale = (avatarWidth - 8) / childrenWidth;
|
|
||||||
} else {
|
|
||||||
this.scale = 1;
|
|
||||||
}
|
|
||||||
this.$forceUpdate();
|
|
||||||
}
|
}
|
||||||
|
const childrenWidth = this.$refs.avatarChildren.offsetWidth; // offsetWidth avoid affecting be transform scale
|
||||||
|
const nodeWidth = this.$refs.avatarNode.offsetWidth;
|
||||||
|
// denominator is 0 is no meaning
|
||||||
|
if (
|
||||||
|
childrenWidth === 0 ||
|
||||||
|
nodeWidth === 0 ||
|
||||||
|
(this.lastChildrenWidth === childrenWidth && this.lastNodeWidth === nodeWidth)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.lastChildrenWidth = childrenWidth;
|
||||||
|
this.lastNodeWidth = nodeWidth;
|
||||||
|
// add 4px gap for each side to get better performance
|
||||||
|
this.scale = nodeWidth - 8 < childrenWidth ? (nodeWidth - 8) / childrenWidth : 1;
|
||||||
},
|
},
|
||||||
handleImgLoadError() {
|
handleImgLoadError() {
|
||||||
const { loadError } = this.$props;
|
const { loadError } = this.$props;
|
||||||
|
@ -88,12 +87,12 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { prefixCls: customizePrefixCls, shape, size, src, icon, alt, srcSet } = this.$props;
|
const { prefixCls: customizePrefixCls, shape, size, src, alt, srcSet } = this.$props;
|
||||||
|
const icon = getComponentFromProp(this, 'icon');
|
||||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||||
const prefixCls = getPrefixCls('avatar', customizePrefixCls);
|
const prefixCls = getPrefixCls('avatar', customizePrefixCls);
|
||||||
|
|
||||||
const { isImgExist, scale } = this.$data;
|
const { isImgExist, scale, isMounted } = this.$data;
|
||||||
|
|
||||||
const sizeCls = {
|
const sizeCls = {
|
||||||
[`${prefixCls}-lg`]: size === 'large',
|
[`${prefixCls}-lg`]: size === 'large',
|
||||||
|
@ -122,10 +121,14 @@ export default {
|
||||||
if (src && isImgExist) {
|
if (src && isImgExist) {
|
||||||
children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
|
children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
|
||||||
} else if (icon) {
|
} else if (icon) {
|
||||||
children = <Icon type={icon} />;
|
if (typeof icon === 'string') {
|
||||||
|
children = <Icon type={icon} />;
|
||||||
|
} else {
|
||||||
|
children = icon;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const childrenNode = this.$refs.avatarChildren;
|
const childrenNode = this.$refs.avatarChildren;
|
||||||
if (childrenNode || (scale !== 1 && childrenNode)) {
|
if (childrenNode || scale !== 1) {
|
||||||
const transformString = `scale(${scale}) translateX(-50%)`;
|
const transformString = `scale(${scale}) translateX(-50%)`;
|
||||||
const childrenStyle = {
|
const childrenStyle = {
|
||||||
msTransform: transformString,
|
msTransform: transformString,
|
||||||
|
@ -148,15 +151,21 @@ export default {
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
const childrenStyle = {};
|
||||||
|
if (!isMounted) {
|
||||||
|
childrenStyle.opacity = 0;
|
||||||
|
}
|
||||||
children = (
|
children = (
|
||||||
<span class={`${prefixCls}-string`} ref="avatarChildren">
|
<span class={`${prefixCls}-string`} ref="avatarChildren" style={{ opacity: 0 }}>
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span {...{ on: getListeners(this), class: classString, style: sizeStyle }}>{children}</span>
|
<span ref="avatarNode" {...{ on: getListeners(this), class: classString, style: sizeStyle }}>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,26 @@ import { asyncExpect } from '@/tests/utils';
|
||||||
import Avatar from '..';
|
import Avatar from '..';
|
||||||
|
|
||||||
describe('Avatar Render', () => {
|
describe('Avatar Render', () => {
|
||||||
|
let originOffsetWidth;
|
||||||
|
beforeAll(() => {
|
||||||
|
// Mock offsetHeight
|
||||||
|
originOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth').get;
|
||||||
|
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
|
||||||
|
get() {
|
||||||
|
if (this.className === 'ant-avatar-string') {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
return 80;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
// Restore Mock offsetHeight
|
||||||
|
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
|
||||||
|
get: originOffsetWidth,
|
||||||
|
});
|
||||||
|
});
|
||||||
it('Render long string correctly', () => {
|
it('Render long string correctly', () => {
|
||||||
const wrapper = mount(Avatar, {
|
const wrapper = mount(Avatar, {
|
||||||
slots: {
|
slots: {
|
||||||
|
@ -25,6 +45,9 @@ describe('Avatar Render', () => {
|
||||||
attachToDocument: true,
|
attachToDocument: true,
|
||||||
});
|
});
|
||||||
wrapper.vm.setScale = jest.fn(() => {
|
wrapper.vm.setScale = jest.fn(() => {
|
||||||
|
if (wrapper.vm.scale === 0.5) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
wrapper.setData({ scale: 0.5 });
|
wrapper.setData({ scale: 0.5 });
|
||||||
wrapper.vm.$forceUpdate();
|
wrapper.vm.$forceUpdate();
|
||||||
});
|
});
|
||||||
|
@ -35,14 +58,14 @@ describe('Avatar Render', () => {
|
||||||
const children = wrapper.findAll('.ant-avatar-string');
|
const children = wrapper.findAll('.ant-avatar-string');
|
||||||
expect(children.length).toBe(1);
|
expect(children.length).toBe(1);
|
||||||
expect(children.at(0).text()).toBe('Fallback');
|
expect(children.at(0).text()).toBe('Fallback');
|
||||||
expect(wrapper.vm.setScale).toBeCalled();
|
expect(wrapper.vm.setScale).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
await asyncExpect(() => {
|
await asyncExpect(() => {
|
||||||
expect(global.document.body.querySelector('.ant-avatar-string').style.transform).toContain(
|
expect(global.document.body.querySelector('.ant-avatar-string').style.transform).toContain(
|
||||||
'scale(0.5)',
|
'scale(0.5)',
|
||||||
);
|
);
|
||||||
global.document.body.innerHTML = '';
|
global.document.body.innerHTML = '';
|
||||||
}, 0);
|
}, 1000);
|
||||||
});
|
});
|
||||||
it('should handle onError correctly', async () => {
|
it('should handle onError correctly', async () => {
|
||||||
global.document.body.innerHTML = '';
|
global.document.body.innerHTML = '';
|
||||||
|
|
|
@ -9,6 +9,6 @@ exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-lg ant-avatar-square" style="background-color: rgb(245, 106, 0); vertical-align: middle;"><span class="ant-avatar-string">U</span></span> <button type="button" class="ant-btn ant-btn-sm" style="vertical-align: middle;"><span>改 变</span></button></div>`;
|
exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-lg ant-avatar-square" style="background-color: rgb(245, 106, 0); vertical-align: middle;"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <button type="button" class="ant-btn ant-btn-sm" style="vertical-align: middle;"><span>改 变</span></button></div>`;
|
||||||
|
|
||||||
exports[`renders ./components/avatar/demo/type.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string">U</span></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string">USER</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span> <span class="ant-avatar ant-avatar-circle" style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"><span class="ant-avatar-string">U</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon" style="background-color: rgb(135, 208, 104);"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span></div>`;
|
exports[`renders ./components/avatar/demo/type.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i slot="icon" aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string" style="opacity: 0;">USER</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span> <span class="ant-avatar ant-avatar-circle" style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon" style="background-color: rgb(135, 208, 104);"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span></div>`;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<us>
|
<us>
|
||||||
#### With Badge
|
#### With Badge
|
||||||
Usually used for messages remind.
|
Usually used for reminders and notifications.
|
||||||
</us>
|
</us>
|
||||||
|
|
||||||
```tpl
|
```tpl
|
||||||
|
|
|
@ -12,6 +12,9 @@ Image, Icon and letter are supported, and the latter two kinds avatar can have c
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a-avatar icon="user" />
|
<a-avatar icon="user" />
|
||||||
|
<a-avatar>
|
||||||
|
<a-icon type="user" slot="icon" />
|
||||||
|
</a-avatar>
|
||||||
<a-avatar>U</a-avatar>
|
<a-avatar>U</a-avatar>
|
||||||
<a-avatar>USER</a-avatar>
|
<a-avatar>USER</a-avatar>
|
||||||
<a-avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
|
<a-avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| icon | 设置头像的图标类型,参考 `Icon` 组件 | string | - |
|
| icon | 设置头像的图标类型,可设为 Icon 的 `type` 或 VNode | string \| VNode \| slot | - |
|
||||||
| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | `circle` |
|
| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | `circle` |
|
||||||
| size | 设置头像的大小 | number \| Enum{ 'large', 'small', 'default' } | `default` |
|
| size | 设置头像的大小 | number \| Enum{ 'large', 'small', 'default' } | `default` |
|
||||||
| src | 图片类头像的资源地址 | string | - |
|
| src | 图片类头像的资源地址 | string | - |
|
||||||
|
|
Loading…
Reference in New Issue