group.tsx 5.0 KB

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