ol-button.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { forwardRef } from 'react'
  2. import BootstrapVersionSwitcher from '../bootstrap-5/bootstrap-version-switcher'
  3. import { Button as BS3Button } from 'react-bootstrap'
  4. import type { ButtonProps } from '@/features/ui/components/types/button-props'
  5. import type { ButtonProps as BS3ButtonPropsBase } from 'react-bootstrap'
  6. import Button from '../bootstrap-5/button'
  7. import classnames from 'classnames'
  8. import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
  9. import { callFnsInSequence } from '@/utils/functions'
  10. import Icon from '@/shared/components/icon'
  11. export type BS3ButtonSize = 'xsmall' | 'sm' | 'medium' | 'lg'
  12. export type OLButtonProps = ButtonProps & {
  13. bs3Props?: {
  14. loading?: React.ReactNode
  15. bsSize?: BS3ButtonSize
  16. block?: boolean
  17. className?: string
  18. onMouseOver?: React.MouseEventHandler<HTMLButtonElement>
  19. onMouseOut?: React.MouseEventHandler<HTMLButtonElement>
  20. onFocus?: React.FocusEventHandler<HTMLButtonElement>
  21. onBlur?: React.FocusEventHandler<HTMLButtonElement>
  22. }
  23. }
  24. // Resolve type mismatch of the onClick event handler
  25. export type BS3ButtonProps = Omit<BS3ButtonPropsBase, 'onClick'> & {
  26. onClick?: React.MouseEventHandler<any>
  27. }
  28. export function bs3ButtonProps(props: ButtonProps) {
  29. const bs3ButtonProps: BS3ButtonProps = {
  30. bsStyle: null,
  31. bsSize: props.size,
  32. className: classnames(`btn-${props.variant || 'primary'}`, props.className),
  33. disabled: props.isLoading || props.disabled,
  34. form: props.form,
  35. href: props.href,
  36. id: props.id,
  37. target: props.target,
  38. rel: props.rel,
  39. onClick: props.onClick,
  40. onMouseDown: props.onMouseDown as BS3ButtonProps['onMouseDown'],
  41. type: props.type,
  42. draggable: props.draggable,
  43. download: props.download,
  44. style: props.style,
  45. active: props.active,
  46. }
  47. return bs3ButtonProps
  48. }
  49. function BS3ButtonContent({
  50. children,
  51. leadingIcon,
  52. trailingIcon,
  53. }: {
  54. children: React.ReactNode
  55. leadingIcon: OLButtonProps['leadingIcon']
  56. trailingIcon: OLButtonProps['trailingIcon']
  57. }) {
  58. const leadingIconComponent =
  59. leadingIcon && typeof leadingIcon === 'string' ? (
  60. <Icon type={leadingIcon} />
  61. ) : (
  62. leadingIcon
  63. )
  64. const trailingIconComponent =
  65. trailingIcon && typeof trailingIcon === 'string' ? (
  66. <Icon type={trailingIcon} />
  67. ) : (
  68. trailingIcon
  69. )
  70. return (
  71. <>
  72. {leadingIconComponent ? <>{leadingIconComponent}&nbsp;</> : null}
  73. {children}
  74. {trailingIconComponent ? <>&nbsp;{trailingIconComponent}</> : null}
  75. </>
  76. )
  77. }
  78. const OLButton = forwardRef<HTMLButtonElement, OLButtonProps>(
  79. ({ bs3Props = {}, ...rest }, ref) => {
  80. const { className: _, ...restBs3Props } = bs3Props
  81. // BS3 OverlayTrigger automatically provides 'onMouseOver', 'onMouseOut', 'onFocus', 'onBlur' event handlers
  82. const bs3FinalProps = {
  83. ...restBs3Props,
  84. onMouseOver: callFnsInSequence(bs3Props?.onMouseOver, rest.onMouseOver),
  85. onMouseOut: callFnsInSequence(bs3Props?.onMouseOut, rest.onMouseOut),
  86. onFocus: callFnsInSequence(bs3Props?.onFocus, rest.onFocus),
  87. onBlur: callFnsInSequence(bs3Props?.onBlur, rest.onBlur),
  88. }
  89. // Get all `aria-*` and `data-*` attributes
  90. const extraProps = getAriaAndDataProps(rest)
  91. return (
  92. <BootstrapVersionSwitcher
  93. bs3={
  94. <BS3Button
  95. {...bs3ButtonProps({
  96. ...rest,
  97. // Override the `className` with bs3 specific className (if provided)
  98. className: bs3Props?.className ?? rest.className,
  99. })}
  100. {...extraProps}
  101. {...bs3FinalProps}
  102. ref={ref as React.LegacyRef<any> | undefined}
  103. >
  104. {bs3Props?.loading || (
  105. <BS3ButtonContent
  106. leadingIcon={rest.leadingIcon}
  107. trailingIcon={rest.trailingIcon}
  108. >
  109. {rest.children}
  110. </BS3ButtonContent>
  111. )}
  112. </BS3Button>
  113. }
  114. bs5={<Button {...rest} ref={ref} />}
  115. />
  116. )
  117. }
  118. )
  119. OLButton.displayName = 'OLButton'
  120. export default OLButton