1
0

avatarGroup.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import React, { PureComponent, Fragment, ReactElement } from 'react';
  2. import cls from 'classnames';
  3. import PropTypes from 'prop-types';
  4. import { get as lodashGet, isFunction, isNumber } from 'lodash';
  5. import Avatar from './index';
  6. import { AvatarGroupProps } from './interface';
  7. import { cssClasses, strings } from '@douyinfe/semi-foundation/avatar/constants';
  8. const sizeSet = strings.SIZE;
  9. const shapeSet = strings.SHAPE;
  10. const overlapFromSet = strings.OVERLAP_FROM;
  11. const prefixCls = cssClasses.PREFIX;
  12. export default class AvatarGroup extends PureComponent<AvatarGroupProps> {
  13. static defaultProps = {
  14. size: 'medium',
  15. shape: 'circle',
  16. overlapFrom: 'start',
  17. };
  18. static propTypes = {
  19. children: PropTypes.node,
  20. shape: PropTypes.oneOf(shapeSet),
  21. size: PropTypes.oneOf(sizeSet),
  22. maxCount: PropTypes.number,
  23. renderMore: PropTypes.func,
  24. overlapFrom: PropTypes.oneOf(overlapFromSet),
  25. };
  26. getAllAvatars() {
  27. const { children } = this.props;
  28. if (children) {
  29. return Array.isArray(children) ? React.Children.toArray(children) : [children];
  30. }
  31. return [];
  32. }
  33. getMergeAvatars(avatars: React.ReactNode[]) {
  34. const { maxCount } = this.props;
  35. let renderAvatars = avatars;
  36. const restNumber = avatars.length - maxCount;
  37. const normalAvatars = avatars.slice(0, maxCount);
  38. const restAvatars = avatars.slice(maxCount);
  39. if (restNumber > 0) {
  40. const more = this.renderMoreAvatar(restNumber, restAvatars);
  41. normalAvatars.push(more);
  42. renderAvatars = normalAvatars;
  43. }
  44. return renderAvatars;
  45. }
  46. renderMoreAvatar(restNumber: number, restAvatars: React.ReactNode[]) {
  47. const { renderMore } = this.props;
  48. const moreCls = cls(`${prefixCls}-item-more`);
  49. const restAvatarAlt = restAvatars?.reduce((pre, cur) => {
  50. const { children, alt } = (cur as ReactElement).props;
  51. const avatarInfo = alt ?? ((typeof children === 'string') ? children : '');
  52. if (avatarInfo.length === 0) {
  53. return pre;
  54. }
  55. return (pre as string).length > 0 ? `${pre},${avatarInfo}` : avatarInfo;
  56. }, '');
  57. const finalAlt = ` Number of remaining Avatars:${restNumber},${restAvatarAlt}`;
  58. let moreAvatar = <Avatar className={moreCls} key="_+n" alt={finalAlt}>{`+${restNumber}`}</Avatar>;
  59. if (isFunction(renderMore)) {
  60. moreAvatar = <Fragment key="_+n">{renderMore(restNumber, restAvatars)}</Fragment>;
  61. }
  62. return moreAvatar;
  63. }
  64. render() {
  65. // eslint-disable-next-line no-unused-vars
  66. const { children, maxCount, overlapFrom, size, shape, renderMore, ...rest } = this.props;
  67. let inner;
  68. const groupCls = cls({
  69. [`${prefixCls}-group`]: true,
  70. });
  71. if (children) {
  72. const avatars = this.getAllAvatars();
  73. inner = (isNumber(maxCount) ? this.getMergeAvatars(avatars) : avatars).map((itm, index) => {
  74. const className = cls(lodashGet((itm as any).props, 'className'), {
  75. [`${prefixCls}-item-start-${index}`]: overlapFrom === 'start',
  76. [`${prefixCls}-item-end-${index}`]: overlapFrom === 'end',
  77. });
  78. return React.cloneElement((itm as any), { ...rest, className, size, shape, key: index });
  79. });
  80. }
  81. return <div className={groupCls} role='list'>{inner}</div>;
  82. }
  83. }