group.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import React, { PureComponent } from 'react';
  2. import classNames from 'classnames';
  3. import PropTypes from 'prop-types';
  4. import { cssClasses, strings } from '@douyinfe/semi-foundation/tag/constants';
  5. import Tag from './index';
  6. import Popover from '../popover/index';
  7. import { AvatarShape, TagProps, TagGroupProps } from './interface';
  8. const prefixCls = cssClasses.PREFIX;
  9. const tagSize = strings.TAG_SIZE;
  10. const avatarShapeSet = strings.AVATAR_SHAPE;
  11. export default class TagGroup<T> extends PureComponent<TagGroupProps<T>> {
  12. static defaultProps = {
  13. style: {},
  14. className: '',
  15. size: tagSize[0],
  16. avatarShape: 'square',
  17. onTagClose: () => undefined,
  18. onPlusNMouseEnter: () => undefined,
  19. };
  20. static propTypes = {
  21. children: PropTypes.node,
  22. style: PropTypes.object,
  23. className: PropTypes.string,
  24. maxTagCount: PropTypes.number,
  25. restCount: PropTypes.number,
  26. tagList: PropTypes.array,
  27. size: PropTypes.oneOf(tagSize),
  28. mode: PropTypes.string,
  29. onTagClose: PropTypes.func,
  30. showPopover: PropTypes.bool,
  31. popoverProps: PropTypes.object,
  32. avatarShape: PropTypes.oneOf(avatarShapeSet),
  33. };
  34. renderNTag(n: number, restTags: React.ReactNode) {
  35. const { size, showPopover, popoverProps, onPlusNMouseEnter } = this.props;
  36. let nTag = (
  37. <Tag
  38. closable={false}
  39. size={size}
  40. color="grey"
  41. style={{ backgroundColor: 'transparent' }}
  42. key="_+n"
  43. onMouseEnter={onPlusNMouseEnter}
  44. >
  45. +{n}
  46. </Tag>
  47. );
  48. if (showPopover) {
  49. nTag = (
  50. <Popover
  51. showArrow
  52. content={restTags}
  53. trigger="hover"
  54. position="top"
  55. autoAdjustOverflow
  56. className={`${prefixCls}-rest-group-popover`}
  57. {...popoverProps}
  58. key="_+n_Popover"
  59. >
  60. {nTag}
  61. </Popover>
  62. );
  63. }
  64. return nTag;
  65. }
  66. renderMergeTags(tags: (Tag | React.ReactNode)[]) {
  67. const { maxTagCount, tagList, restCount } = this.props;
  68. const n = restCount ? restCount : tagList.length - maxTagCount;
  69. let renderTags: (Tag | React.ReactNode)[] = tags;
  70. const normalTags: (Tag | React.ReactNode)[] = tags.slice(0, maxTagCount);
  71. const restTags = tags.slice(maxTagCount) as React.ReactNode;
  72. let nTag = null;
  73. if (n > 0) {
  74. nTag = this.renderNTag(n, restTags);
  75. normalTags.push(nTag);
  76. renderTags = normalTags;
  77. }
  78. return renderTags;
  79. }
  80. renderAllTags() {
  81. const { tagList, size, mode, avatarShape, onTagClose } = this.props;
  82. const renderTags = tagList.map((tag): (Tag | React.ReactNode) => {
  83. if (mode === 'custom') {
  84. return tag as React.ReactNode;
  85. }
  86. const newTag = { ...(tag as TagProps) };
  87. if (!(newTag as TagProps).size) {
  88. (newTag as TagProps).size = size;
  89. }
  90. if (!(newTag as TagProps).avatarShape) {
  91. (newTag as TagProps).avatarShape = avatarShape;
  92. }
  93. if (!(newTag as TagProps).tagKey) {
  94. if (typeof (newTag as TagProps).children === 'string' || typeof (newTag as TagProps).children === 'number') {
  95. (newTag as TagProps).tagKey = (newTag as TagProps).children as string | number;
  96. } else {
  97. (newTag as TagProps).tagKey = Math.random();
  98. }
  99. }
  100. return <Tag {...(newTag as TagProps)} key={(newTag as TagProps).tagKey} onClose={(tagChildren, e, tagKey) => {
  101. if ((newTag as TagProps).onClose) {
  102. (newTag as TagProps).onClose(tagChildren, e, tagKey);
  103. }
  104. onTagClose && onTagClose(tagChildren, e, tagKey);
  105. }} />;
  106. });
  107. return renderTags;
  108. }
  109. render() {
  110. const { style, className, maxTagCount, size } = this.props;
  111. const groupCls = classNames({
  112. [`${prefixCls}-group`]: true,
  113. [`${prefixCls}-group-max`]: maxTagCount,
  114. [`${prefixCls}-group-small`]: size === 'small',
  115. [`${prefixCls}-group-large`]: size === 'large',
  116. }, className);
  117. const tags = this.renderAllTags();
  118. const tagContents = (typeof maxTagCount === 'undefined' ? tags : this.renderMergeTags(tags)) as React.ReactNode;
  119. return (
  120. <div style={style} className={groupCls}>
  121. {tagContents}
  122. </div>
  123. );
  124. }
  125. }