buttonGroup.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import React, { isValidElement, cloneElement, ReactNode } from 'react';
  2. import BaseComponent, { BaseProps } from '../_base/baseComponent';
  3. import PropTypes from 'prop-types';
  4. import classNames from 'classnames';
  5. import { cssClasses, strings } from '@douyinfe/semi-foundation/button/constants';
  6. import { get } from 'lodash';
  7. import { Type, Size, ButtonProps } from './Button';
  8. import '@douyinfe/semi-foundation/button/button.scss';
  9. export type Theme = 'solid' | 'borderless' | 'light' | 'outline';
  10. export interface ButtonGroupProps extends BaseProps {
  11. disabled?: boolean;
  12. type?: Type;
  13. size?: Size;
  14. theme?: Theme;
  15. className?: string;
  16. children?: React.ReactNode;
  17. 'aria-label'?: React.AriaAttributes['aria-label']
  18. }
  19. const prefixCls = cssClasses.PREFIX;
  20. const btnSizes = strings.sizes;
  21. export default class ButtonGroup extends BaseComponent<ButtonGroupProps> {
  22. static propTypes = {
  23. children: PropTypes.node,
  24. disabled: PropTypes.bool,
  25. type: PropTypes.string,
  26. size: PropTypes.oneOf(btnSizes),
  27. theme: PropTypes.oneOf(strings.themes),
  28. 'aria-label': PropTypes.string,
  29. };
  30. static defaultProps = {
  31. // There are default values ​​for type and theme in Button.
  32. // In order to allow users to individually customize the type and theme of the Button through the parameters of the Button in the ButtonGroup,
  33. // the default value of type and theme is not given in the ButtonGroup。
  34. size: 'default',
  35. };
  36. getInnerWithLine(inner) {
  37. const innerWithLine: ReactNode[] = [];
  38. if (inner.length > 1) {
  39. inner.slice(0, -1).forEach((item, index) => {
  40. const isButtonType = get(item, 'type.elementType') === 'Button';
  41. const buttonProps = get(item, 'props') as ButtonProps;
  42. const { type, theme, disabled } = buttonProps ?? {};
  43. if (isButtonType && theme !== 'outline') {
  44. const lineCls = classNames(
  45. `${prefixCls}-group-line`,
  46. `${prefixCls}-group-line-${theme ?? 'light'}`,
  47. `${prefixCls}-group-line-${type ?? 'primary'}`,
  48. {
  49. [`${prefixCls}-group-line-disabled`]: disabled,
  50. }
  51. );
  52. innerWithLine.push(item, <span className={lineCls} key={`line-${index}`} />);
  53. } else {
  54. innerWithLine.push(item);
  55. }
  56. });
  57. innerWithLine.push(inner.slice(-1));
  58. return innerWithLine;
  59. } else {
  60. return inner;
  61. }
  62. }
  63. render() {
  64. const { children, disabled, size, type, className, style, 'aria-label': ariaLabel, ...rest } = this.props;
  65. let inner: ReactNode[];
  66. let innerWithLine: ReactNode[] = [];
  67. const cls = classNames(`${prefixCls}-group`, className);
  68. if (children) {
  69. inner = ((Array.isArray(children) ? children : [children])).map((itm: React.ReactNode, index) => (
  70. isValidElement(itm)
  71. ? cloneElement(itm, { disabled, size, type, ...itm.props, ...rest, key: itm.key ?? index })
  72. : itm
  73. ));
  74. innerWithLine = this.getInnerWithLine(inner);
  75. }
  76. return <div className={cls} style={style} role="group" aria-label={ariaLabel}>{innerWithLine}</div>;
  77. }
  78. }