split-menu.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import { Button, Dropdown, MenuItem } from 'react-bootstrap'
  2. import type {
  3. ButtonProps,
  4. MenuItemProps,
  5. DropdownButtonProps,
  6. DropdownProps,
  7. } from 'react-bootstrap'
  8. import type { PropsWithChildren } from 'react'
  9. import classNames from 'classnames'
  10. import Tooltip, { type TooltipProps } from './tooltip'
  11. import Icon, { type IconProps } from './icon'
  12. import type { BsSize, BsStyle } from '../../../../types/bootstrap'
  13. type SplitMenuBsStyle = Extract<BsStyle, 'primary' | 'secondary' | 'danger'>
  14. type SplitMenuBsSize = Extract<BsSize, 'md' | 'sm' | 'xs'>
  15. type SplitMenuButtonProps = {
  16. tooltip?: Omit<TooltipProps, 'children'>
  17. bsStyle?: SplitMenuBsStyle
  18. text: string
  19. icon?: IconProps
  20. } & Pick<ButtonProps, 'aria-label' | 'onClick' | 'className' | 'disabled'>
  21. type SplitMenuDropdownToggleProps = {
  22. handleAnimationEnd?: () => void
  23. } & Pick<DropdownButtonProps, 'className' | 'aria-label'>
  24. type SplitMenuDropdownProps = Pick<DropdownProps, 'id' | 'className'>
  25. type SplitMenuProps = PropsWithChildren<{
  26. bsStyle: SplitMenuBsStyle
  27. bsSize?: SplitMenuBsSize
  28. button: Omit<SplitMenuButtonProps, 'disabled'>
  29. dropdown: SplitMenuDropdownProps
  30. dropdownToggle?: SplitMenuDropdownToggleProps
  31. disabled?: boolean
  32. }>
  33. function SplitMenu({
  34. bsStyle,
  35. bsSize = 'md',
  36. button,
  37. dropdown,
  38. dropdownToggle,
  39. disabled = false,
  40. children,
  41. }: SplitMenuProps) {
  42. const { tooltip, icon, ...buttonProps } = button
  43. const splitMenuClassName = classNames('split-menu', {
  44. [`btn-${bsSize}`]: true,
  45. })
  46. const dropdownToggleClassName = classNames(
  47. 'split-menu-dropdown-toggle',
  48. dropdownToggle?.className
  49. )
  50. return (
  51. <div className={splitMenuClassName}>
  52. <SplitMenuButton
  53. // eslint-disable-next-line react/jsx-handler-names
  54. onClick={buttonProps.onClick}
  55. className={buttonProps.className}
  56. disabled={disabled}
  57. tooltip={tooltip}
  58. bsStyle={bsStyle}
  59. >
  60. {icon ? (
  61. <Icon className="split-menu-icon" type={icon.type} spin={icon.spin} />
  62. ) : null}
  63. <span className="split-menu-button">{buttonProps.text}</span>
  64. </SplitMenuButton>
  65. <Dropdown
  66. className={classNames('split-menu-dropdown', dropdown.className)}
  67. id={dropdown.id}
  68. >
  69. <Dropdown.Toggle
  70. aria-label={dropdownToggle?.['aria-label']}
  71. className={dropdownToggleClassName}
  72. bsStyle={bsStyle}
  73. onAnimationEnd={dropdownToggle?.handleAnimationEnd}
  74. data-ol-loading={disabled}
  75. />
  76. <Dropdown.Menu>{children}</Dropdown.Menu>
  77. </Dropdown>
  78. </div>
  79. )
  80. }
  81. function SplitMenuButton({
  82. onClick,
  83. disabled,
  84. tooltip,
  85. bsStyle,
  86. children,
  87. className,
  88. ...props
  89. }: PropsWithChildren<Omit<SplitMenuButtonProps, 'text' | 'icon'>>) {
  90. const buttonClassName = classNames('split-menu-button', className)
  91. if (tooltip) {
  92. return (
  93. <Tooltip
  94. id={tooltip.id}
  95. description={tooltip.description}
  96. tooltipProps={tooltip.tooltipProps}
  97. overlayProps={tooltip.overlayProps}
  98. >
  99. <Button
  100. className={buttonClassName}
  101. bsStyle={bsStyle}
  102. onClick={onClick}
  103. aria-label={props['aria-label']}
  104. disabled={disabled}
  105. data-ol-loading={disabled}
  106. >
  107. {children}
  108. </Button>
  109. </Tooltip>
  110. )
  111. }
  112. return (
  113. <Button
  114. className={buttonClassName}
  115. bsStyle={bsStyle}
  116. onClick={onClick}
  117. aria-label={props['aria-label']}
  118. disabled={disabled}
  119. data-ol-loading={disabled}
  120. >
  121. {children}
  122. </Button>
  123. )
  124. }
  125. function SplitMenuItem(props: MenuItemProps) {
  126. return <MenuItem {...props} draggable="false" />
  127. }
  128. SplitMenu.Item = SplitMenuItem
  129. export default SplitMenu