button.tsx 2.1 KB

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