button.tsx 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { forwardRef } from 'react'
  2. import { Button as BS5Button, Spinner } from 'react-bootstrap-5'
  3. import type { ButtonProps } from '@/features/ui/components/types/button-props'
  4. import classNames from 'classnames'
  5. import { useTranslation } from 'react-i18next'
  6. import MaterialIcon from '@/shared/components/material-icon'
  7. const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  8. (
  9. {
  10. children,
  11. className,
  12. leadingIcon,
  13. isLoading = false,
  14. loadingLabel,
  15. trailingIcon,
  16. variant = 'primary',
  17. ...props
  18. },
  19. ref
  20. ) => {
  21. const { t } = useTranslation()
  22. const buttonClassName = classNames('d-inline-grid', className, {
  23. 'button-loading': isLoading,
  24. })
  25. const loadingSpinnerClassName =
  26. props.size === 'lg' ? 'loading-spinner-large' : 'loading-spinner-small'
  27. const materialIconClassName =
  28. props.size === 'lg' ? 'icon-large' : 'icon-small'
  29. const leadingIconComponent =
  30. leadingIcon && typeof leadingIcon === 'string' ? (
  31. <MaterialIcon type={leadingIcon} className={materialIconClassName} />
  32. ) : (
  33. leadingIcon
  34. )
  35. const trailingIconComponent =
  36. trailingIcon && typeof trailingIcon === 'string' ? (
  37. <MaterialIcon type={trailingIcon} className={materialIconClassName} />
  38. ) : (
  39. trailingIcon
  40. )
  41. return (
  42. <BS5Button
  43. className={buttonClassName}
  44. variant={variant}
  45. {...props}
  46. ref={ref}
  47. disabled={isLoading || props.disabled}
  48. data-ol-loading={isLoading}
  49. >
  50. {isLoading && (
  51. <span className="spinner-container">
  52. <Spinner
  53. animation="border"
  54. aria-hidden="true"
  55. as="span"
  56. className={loadingSpinnerClassName}
  57. role="status"
  58. />
  59. <span className="visually-hidden">
  60. {loadingLabel ?? t('loading')}
  61. </span>
  62. </span>
  63. )}
  64. <span className="button-content" aria-hidden={isLoading}>
  65. {leadingIconComponent}
  66. {children}
  67. {trailingIconComponent}
  68. </span>
  69. </BS5Button>
  70. )
  71. }
  72. )
  73. Button.displayName = 'Button'
  74. export default Button