mirror of https://github.com/msgbyte/tailchat
refactor: move avatar into design
parent
a655a0d136
commit
5245c49635
@ -0,0 +1,50 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||||
|
|
||||||
|
import { Avatar } from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Tailchat/Avatar',
|
||||||
|
component: Avatar,
|
||||||
|
argTypes: {
|
||||||
|
name: {
|
||||||
|
description: '显示名称,用于无图片下的展示',
|
||||||
|
},
|
||||||
|
isOnline: {
|
||||||
|
description: '是否在线, 可不传',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
description: '图标大小',
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
src: {
|
||||||
|
description: '头像图片地址',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ComponentMeta<typeof Avatar>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Avatar> = (args) => <Avatar {...args} />;
|
||||||
|
|
||||||
|
export const normal = Template.bind({});
|
||||||
|
normal.args = {
|
||||||
|
name: 'Anonymous',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withSize = Template.bind({});
|
||||||
|
withSize.args = {
|
||||||
|
name: 'Anonymous',
|
||||||
|
size: 48,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withOnline = Template.bind({});
|
||||||
|
withOnline.args = {
|
||||||
|
name: 'Anonymous',
|
||||||
|
isOnline: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withImage = Template.bind({});
|
||||||
|
withImage.args = {
|
||||||
|
name: 'Anonymous',
|
||||||
|
src: 'http://dummyimage.com/50x50',
|
||||||
|
};
|
@ -0,0 +1,76 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { Avatar as AntdAvatar, Badge } from 'antd';
|
||||||
|
import _head from 'lodash/head';
|
||||||
|
import _upperCase from 'lodash/upperCase';
|
||||||
|
import _isNil from 'lodash/isNil';
|
||||||
|
import _isEmpty from 'lodash/isEmpty';
|
||||||
|
import _isNumber from 'lodash/isNumber';
|
||||||
|
import type { AvatarProps as AntdAvatarProps } from 'antd/lib/avatar';
|
||||||
|
import { getTextColorHex } from './utils';
|
||||||
|
import { isValidStr } from '../utils';
|
||||||
|
|
||||||
|
interface AvatarProps extends AntdAvatarProps {
|
||||||
|
name?: string;
|
||||||
|
isOnline?: boolean;
|
||||||
|
}
|
||||||
|
export const Avatar: React.FC<AvatarProps> = React.memo((_props) => {
|
||||||
|
const { isOnline, ...props } = _props;
|
||||||
|
const src = isValidStr(props.src) ? props.src : undefined;
|
||||||
|
|
||||||
|
const name = useMemo(() => _upperCase(_head(props.name)), [props.name]);
|
||||||
|
|
||||||
|
const color = useMemo(
|
||||||
|
() =>
|
||||||
|
// 如果src为空 且 icon为空 则给个固定颜色
|
||||||
|
_isEmpty(src) && _isNil(props.icon)
|
||||||
|
? getTextColorHex(props.name)
|
||||||
|
: undefined,
|
||||||
|
[src, props.icon, props.name]
|
||||||
|
);
|
||||||
|
|
||||||
|
const style: React.CSSProperties = useMemo(
|
||||||
|
() => ({
|
||||||
|
cursor: 'inherit',
|
||||||
|
userSelect: 'none',
|
||||||
|
...props.style,
|
||||||
|
backgroundColor: color,
|
||||||
|
}),
|
||||||
|
[props.style, color]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_isNumber(props.size) && typeof style.fontSize === 'undefined') {
|
||||||
|
// 如果props.size是数字且没有指定文字大小
|
||||||
|
// 则自动增加fontSize大小
|
||||||
|
style.fontSize = props.size * 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inner = (
|
||||||
|
<AntdAvatar {...props} src={src} style={style}>
|
||||||
|
{name}
|
||||||
|
</AntdAvatar>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof isOnline === 'boolean') {
|
||||||
|
const style = {
|
||||||
|
bottom: 0,
|
||||||
|
top: 'auto',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isOnline === true) {
|
||||||
|
return (
|
||||||
|
<Badge dot={true} color="green" style={style}>
|
||||||
|
{inner}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Badge dot={true} color="#999" style={style}>
|
||||||
|
{inner}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inner;
|
||||||
|
});
|
||||||
|
Avatar.displayName = 'Avatar';
|
@ -0,0 +1,34 @@
|
|||||||
|
import _isString from 'lodash/isString';
|
||||||
|
import str2int from 'str2int';
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
'#333333',
|
||||||
|
'#2c3e50',
|
||||||
|
'#8e44ad',
|
||||||
|
'#2980b9',
|
||||||
|
'#27ae60',
|
||||||
|
'#16a085',
|
||||||
|
'#f39c12',
|
||||||
|
'#d35400',
|
||||||
|
'#c0392b',
|
||||||
|
'#3498db',
|
||||||
|
'#9b59b6',
|
||||||
|
'#2ecc71',
|
||||||
|
'#1abc9c',
|
||||||
|
'#f1c40f',
|
||||||
|
'#e74c3c',
|
||||||
|
'#e67e22',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文本内容返回一个内置色卡的颜色
|
||||||
|
* @param text 文本
|
||||||
|
*/
|
||||||
|
export function getTextColorHex(text: unknown): string {
|
||||||
|
if (!text || !_isString(text)) {
|
||||||
|
return '#ffffff'; // 如果获取不到文本,则返回白色
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = str2int(text);
|
||||||
|
return colors[id % colors.length];
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* 是否一个可用的字符串
|
||||||
|
* 定义为有长度的字符串
|
||||||
|
*/
|
||||||
|
export function isValidStr(str: unknown): str is string {
|
||||||
|
return typeof str == 'string' && str !== '';
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Tailchat 共享配置
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
color: [
|
|
||||||
'#333333',
|
|
||||||
'#2c3e50',
|
|
||||||
'#8e44ad',
|
|
||||||
'#2980b9',
|
|
||||||
'#27ae60',
|
|
||||||
'#16a085',
|
|
||||||
'#f39c12',
|
|
||||||
'#d35400',
|
|
||||||
'#c0392b',
|
|
||||||
'#3498db',
|
|
||||||
'#9b59b6',
|
|
||||||
'#2ecc71',
|
|
||||||
'#1abc9c',
|
|
||||||
'#f1c40f',
|
|
||||||
'#e74c3c',
|
|
||||||
'#e67e22',
|
|
||||||
],
|
|
||||||
};
|
|
@ -1,75 +1 @@
|
|||||||
import React, { useMemo } from 'react';
|
export { Avatar } from 'tailchat-design';
|
||||||
import { Avatar as AntdAvatar, Badge } from 'antd';
|
|
||||||
import _head from 'lodash/head';
|
|
||||||
import _upperCase from 'lodash/upperCase';
|
|
||||||
import _isNil from 'lodash/isNil';
|
|
||||||
import _isEmpty from 'lodash/isEmpty';
|
|
||||||
import _isNumber from 'lodash/isNumber';
|
|
||||||
import type { AvatarProps as AntdAvatarProps } from 'antd/lib/avatar';
|
|
||||||
import { getTextColorHex, isValidStr } from 'tailchat-shared';
|
|
||||||
|
|
||||||
interface AvatarProps extends AntdAvatarProps {
|
|
||||||
name?: string;
|
|
||||||
isOnline?: boolean;
|
|
||||||
}
|
|
||||||
export const Avatar: React.FC<AvatarProps> = React.memo((_props) => {
|
|
||||||
const { isOnline, ...props } = _props;
|
|
||||||
const src = isValidStr(props.src) ? props.src : undefined;
|
|
||||||
|
|
||||||
const name = useMemo(() => _upperCase(_head(props.name)), [props.name]);
|
|
||||||
|
|
||||||
const color = useMemo(
|
|
||||||
() =>
|
|
||||||
// 如果src为空 且 icon为空 则给个固定颜色
|
|
||||||
_isEmpty(src) && _isNil(props.icon)
|
|
||||||
? getTextColorHex(props.name)
|
|
||||||
: undefined,
|
|
||||||
[src, props.icon, props.name]
|
|
||||||
);
|
|
||||||
|
|
||||||
const style: React.CSSProperties = useMemo(
|
|
||||||
() => ({
|
|
||||||
cursor: 'inherit',
|
|
||||||
userSelect: 'none',
|
|
||||||
...props.style,
|
|
||||||
backgroundColor: color,
|
|
||||||
}),
|
|
||||||
[props.style, color]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (_isNumber(props.size) && typeof style.fontSize === 'undefined') {
|
|
||||||
// 如果props.size是数字且没有指定文字大小
|
|
||||||
// 则自动增加fontSize大小
|
|
||||||
style.fontSize = props.size * 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
const inner = (
|
|
||||||
<AntdAvatar {...props} src={src} style={style}>
|
|
||||||
{name}
|
|
||||||
</AntdAvatar>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (typeof isOnline === 'boolean') {
|
|
||||||
const style = {
|
|
||||||
bottom: 0,
|
|
||||||
top: 'auto',
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isOnline === true) {
|
|
||||||
return (
|
|
||||||
<Badge dot={true} color="green" style={style}>
|
|
||||||
{inner}
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Badge dot={true} color="#999" style={style}>
|
|
||||||
{inner}
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inner;
|
|
||||||
});
|
|
||||||
Avatar.displayName = 'Avatar';
|
|
||||||
|
Loading…
Reference in New Issue