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';
 | 
			
		||||
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';
 | 
			
		||||
export { Avatar } from 'tailchat-design';
 | 
			
		||||
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue