import React, { MouseEvent, FocusEvent, ReactElement, ReactChild } from 'react'; import css from '../../utils/css'; import { StyledButton, IconWrapper, Intent, ThemeVariant, } from './StyledButton'; import Icon, { IconName } from '../Icon'; import { CommonProps } from '../common'; import { fromUndefinedable, getOrElse, map, none, some, Option, } from '../../fp/Option'; import { pipe } from '../../fp/function'; export interface ButtonProps extends CommonProps { /** * Disable state of button. */ disabled?: boolean; /** * Icon name to render before the text. */ icon?: IconName; /** * Visual intent color to apply to button. It is required for `filled`, `outlined` and `text` variants. */ intent?: 'primary' | 'danger' | 'success' | 'warning' | 'error'; /** * Loading state of button. */ loading?: boolean; /** * Set handler to handler `blur` event. */ onBlur?: (e: FocusEvent) => void; /** * Set the handler to handle `click` event. */ onClick?: (e: MouseEvent) => void; /** * Icon name to render after the text. */ rightIcon?: IconName; /** * Size of button. */ size?: 'small' | 'medium' | 'large'; /** * Button label. */ text: ReactChild; /** * Specifies the HTML attribute type of button. */ type?: 'submit' | 'reset' | 'button'; /** * Button type. */ variant?: | 'basic' | 'basic-transparent' | 'filled' | 'outlined' | 'text' | 'filled-reversed'; } const FILLED_VARIANTS = { primary: 'filled-primary', success: 'filled-success', danger: 'filled-danger', warning: 'filled-warning', error: 'filled-error', black: 'filled-black', } as const; const OUTLINED_VARIANTS = { primary: 'outlined-primary', success: 'outlined-success', danger: 'outlined-danger', warning: 'outlined-warning', error: 'outlined-error', black: 'outlined-black', } as const; const TEXT_VARIANTS = { primary: 'text-primary', success: 'text-success', danger: 'text-danger', warning: 'text-warning', error: 'text-error', black: 'text-black', } as const; const FILLED_REVERSED_VARIANTS = { primary: 'filled-reversed-primary', success: 'filled-reversed-success', danger: 'filled-reversed-danger', warning: 'filled-reversed-warning', error: 'filled-reversed-error', black: 'filled-reversed-black', } as const; export const getThemeVariant = ( variant: | 'basic' | 'basic-transparent' | 'filled' | 'outlined' | 'text' | 'filled-reversed', intent: Intent ): ThemeVariant => { switch (variant) { case 'basic': case 'basic-transparent': return variant; case 'filled': return FILLED_VARIANTS[intent]; case 'outlined': return OUTLINED_VARIANTS[intent]; case 'text': return TEXT_VARIANTS[intent]; case 'filled-reversed': return FILLED_REVERSED_VARIANTS[intent]; } }; const Button = ({ text, icon, rightIcon, onClick, onBlur, variant = 'filled', intent = 'primary', loading, disabled, size = 'medium', type = 'button', id, className, style, sx = {}, 'data-test-id': dataTestId, }: ButtonProps): ReactElement => { const maybeIcon = fromUndefinedable(icon); const maybeLoading: Option = loading === true ? some('loading') : none; const mapToLeftIcon = map( (ic: IconName): ReactElement => ( ) ); return ( {pipe( maybeIcon, mapToLeftIcon, getOrElse(() => pipe( maybeLoading, mapToLeftIcon, getOrElse(() => null) ) ) )} {text} {rightIcon !== undefined && ( )} ); }; export default Button;