2022-03-21 11:09:01 -07:00
|
|
|
import * as React from 'react';
|
|
|
|
import { Link } from 'react-router-dom';
|
|
|
|
|
2022-04-05 07:42:19 -07:00
|
|
|
import Icon from '../icon/icon';
|
2022-03-21 11:09:01 -07:00
|
|
|
|
|
|
|
import { useButtonStyles } from './useButtonStyles';
|
|
|
|
|
|
|
|
import type { ButtonSizes, ButtonThemes } from './useButtonStyles';
|
|
|
|
|
|
|
|
interface IButton {
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Whether this button expands the width of its container. */
|
2022-03-21 11:09:01 -07:00
|
|
|
block?: boolean,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Elements inside the <button> */
|
2022-03-21 11:09:01 -07:00
|
|
|
children?: React.ReactNode,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** @deprecated unused */
|
2022-03-21 11:09:01 -07:00
|
|
|
classNames?: string,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Prevent the button from being clicked. */
|
2022-03-21 11:09:01 -07:00
|
|
|
disabled?: boolean,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** URL to an SVG icon to render inside the button. */
|
2022-03-21 11:09:01 -07:00
|
|
|
icon?: string,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Action when the button is clicked. */
|
2022-03-21 11:09:01 -07:00
|
|
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** A predefined button size. */
|
2022-03-21 11:09:01 -07:00
|
|
|
size?: ButtonSizes,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** @deprecated unused */
|
2022-03-21 11:09:01 -07:00
|
|
|
style?: React.CSSProperties,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Text inside the button. Takes precedence over `children`. */
|
2022-03-21 11:09:01 -07:00
|
|
|
text?: React.ReactNode,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Makes the button into a navlink, if provided. */
|
2022-03-21 11:09:01 -07:00
|
|
|
to?: string,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Styles the button visually with a predefined theme. */
|
2022-03-21 11:09:01 -07:00
|
|
|
theme?: ButtonThemes,
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Whether this button should submit a form by default. */
|
2022-03-21 11:09:01 -07:00
|
|
|
type?: 'button' | 'submit',
|
|
|
|
}
|
|
|
|
|
2022-04-30 21:39:58 -07:00
|
|
|
/** Customizable button element with various themes. */
|
2022-03-21 11:09:01 -07:00
|
|
|
const Button = React.forwardRef<HTMLButtonElement, IButton>((props, ref): JSX.Element => {
|
|
|
|
const {
|
|
|
|
block = false,
|
|
|
|
children,
|
|
|
|
disabled = false,
|
|
|
|
icon,
|
|
|
|
onClick,
|
|
|
|
size = 'md',
|
|
|
|
text,
|
|
|
|
theme = 'accent',
|
|
|
|
to,
|
|
|
|
type = 'button',
|
|
|
|
} = props;
|
|
|
|
|
|
|
|
const themeClass = useButtonStyles({
|
|
|
|
theme,
|
|
|
|
block,
|
|
|
|
disabled,
|
|
|
|
size,
|
|
|
|
});
|
|
|
|
|
|
|
|
const renderIcon = () => {
|
|
|
|
if (!icon) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-04-05 07:42:19 -07:00
|
|
|
return <Icon src={icon} className='mr-2 w-4 h-4' />;
|
2022-03-21 11:09:01 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleClick = React.useCallback((event) => {
|
|
|
|
if (onClick && !disabled) {
|
|
|
|
onClick(event);
|
|
|
|
}
|
|
|
|
}, [onClick, disabled]);
|
|
|
|
|
|
|
|
const renderButton = () => (
|
|
|
|
<button
|
|
|
|
className={themeClass}
|
|
|
|
disabled={disabled}
|
|
|
|
onClick={handleClick}
|
|
|
|
ref={ref}
|
|
|
|
type={type}
|
2022-04-04 08:53:47 -07:00
|
|
|
data-testid='button'
|
2022-03-21 11:09:01 -07:00
|
|
|
>
|
|
|
|
{renderIcon()}
|
|
|
|
{text || children}
|
|
|
|
</button>
|
|
|
|
);
|
|
|
|
|
|
|
|
if (to) {
|
|
|
|
return (
|
|
|
|
<Link to={to} tabIndex={-1} className='inline-flex'>
|
|
|
|
{renderButton()}
|
|
|
|
</Link>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return renderButton();
|
|
|
|
});
|
|
|
|
|
|
|
|
export default Button;
|