index.tsx 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import React, { PureComponent } from 'react';
  2. import cls from 'classnames';
  3. import PropTypes from 'prop-types';
  4. import { isNumber, isString, noop } from 'lodash';
  5. import ConfigContext, { ContextValue } from '../configProvider/context';
  6. import { cssClasses, strings } from '@douyinfe/semi-foundation/badge/constants';
  7. import '@douyinfe/semi-foundation/badge/badge.scss';
  8. const prefixCls = cssClasses.PREFIX;
  9. export type BadgeType = 'primary' | 'secondary' | 'tertiary' | 'danger' | 'warning';
  10. export type BadgeTheme = 'solid' | 'light' | 'inverted';
  11. export type BadgePosition = 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom';
  12. export interface BadgeProps {
  13. count?: React.ReactNode;
  14. dot?: boolean;
  15. type?: BadgeType;
  16. theme?: BadgeTheme;
  17. position?: BadgePosition;
  18. overflowCount?: number;
  19. style?: React.CSSProperties;
  20. className?: string;
  21. onMouseEnter?: (e: React.MouseEvent) => any;
  22. onMouseLeave?: (e: React.MouseEvent) => any;
  23. onClick?: (e: React.MouseEvent) => any;
  24. children?: React.ReactNode;
  25. }
  26. export default class Badge extends PureComponent<BadgeProps> {
  27. static contextType = ConfigContext;
  28. static propTypes = {
  29. count: PropTypes.node,
  30. dot: PropTypes.bool,
  31. type: PropTypes.oneOf(strings.TYPE_SET),
  32. theme: PropTypes.oneOf(strings.THEME_SET),
  33. position: PropTypes.oneOf(strings.POS_SET),
  34. overflowCount: PropTypes.number,
  35. style: PropTypes.object,
  36. className: PropTypes.string,
  37. children: PropTypes.node,
  38. onClick: PropTypes.func,
  39. onMouseEnter: PropTypes.func,
  40. onMouseLeave: PropTypes.func,
  41. };
  42. static defaultProps = {
  43. dot: false,
  44. type: 'primary',
  45. theme: 'solid',
  46. className: '',
  47. onClick: () => noop,
  48. onMouseEnter: () => noop,
  49. onMouseLeave: () => noop,
  50. };
  51. context: ContextValue;
  52. render() {
  53. const { direction } = this.context;
  54. // DefaultPosition here, static can't get this
  55. const defaultPosition = direction === 'rtl' ? 'leftTop' : 'rightTop';
  56. // eslint-disable-next-line max-len
  57. const { count, dot, type, theme, position = defaultPosition, overflowCount, style, children, className, ...rest } = this.props;
  58. const custom = count && !(isNumber(count) || isString(count));
  59. const showBadge = count !== null && typeof count !== 'undefined';
  60. const wrapper = cls(className, {
  61. [`${prefixCls}-${type}`]: !custom,
  62. [`${prefixCls}-${theme}`]: !custom,
  63. [`${prefixCls}-${position}`]: Boolean(position) && Boolean(children),
  64. [`${prefixCls}-block`]: !children,
  65. [`${prefixCls}-dot`]: dot,
  66. [`${prefixCls}-count`]: !dot && !custom && showBadge,
  67. [`${prefixCls}-custom`]: custom,
  68. });
  69. let content;
  70. if (isNumber(count)) {
  71. content = overflowCount && overflowCount < count ? `${overflowCount}+` : `${count}`;
  72. } else {
  73. content = count;
  74. }
  75. return (
  76. <span className={prefixCls} {...rest}>
  77. {children}
  78. <span className={wrapper} style={style}>
  79. {dot ? null : content}
  80. </span>
  81. </span>
  82. );
  83. }
  84. }