276 lines
7.5 KiB
JavaScript
276 lines
7.5 KiB
JavaScript
import React from 'react'
|
|
import PropTypes from 'prop-types'
|
|
import { NavLink } from 'react-router-dom'
|
|
import { CX } from '../constants'
|
|
import Icon from './icon'
|
|
import Tooltip from './tooltip'
|
|
|
|
// Define colors for enumeration for Button component `color`, `backgroundColor` props
|
|
const COLORS = {
|
|
primary: 'primary',
|
|
secondary: 'secondary',
|
|
tertiary: 'tertiary',
|
|
white: 'white',
|
|
black: 'black',
|
|
brand: 'brand',
|
|
danger: 'danger',
|
|
none: 'none',
|
|
}
|
|
|
|
/**
|
|
* Renders a button component
|
|
* @param {string} [props.backgroundColor='brand'] - background color of the button
|
|
* @param {func|node} [props.buttonRef] - ref to send to button component
|
|
* @param {string} [props.className] - add custom className
|
|
* @param {string} [props.color='white'] - text color of the button
|
|
* @param {string} [props.href] - href to send to on click
|
|
* @param {string} [props.icon] - prepend icon id
|
|
* @param {string} [props.iconClassName] - add custom className to icon
|
|
* @param {string} [props.iconSize] - size of the icon
|
|
* @param {bool} [props.isBlock] - if button is width: 100%
|
|
* @param {bool} [props.isDisabled] - if the button is disabled
|
|
* @param {bool} [props.isNarrow] - if the button is narrow
|
|
* @param {bool} [props.isOutline] - if the button is outline design
|
|
* @param {bool} [props.noClasses] - if the button has no default classes
|
|
* @param {func} [props.onClick] - function to call on button click
|
|
* @param {func} [props.onMouseEnter] - function to call on button mouse enter
|
|
* @param {func} [props.onMouseLeave] - function to call on button mouse leave
|
|
* @param {bool} [props.radiusSmall] - if the button has small radius
|
|
* @param {bool} [props.rel] - rel for the button
|
|
* @param {bool} [props.target] - target for the button
|
|
* @param {bool} [props.text] - if the button is just text (i.e. link)
|
|
* @param {bool} [props.title] - `title` attribute for button
|
|
* @param {bool} [props.to] - `to` to send to on click
|
|
* @param {bool} [props.tooltip] - add a tooltip to the button on hover/click
|
|
* @param {bool} [props.type] - `type` attribute for button
|
|
* @param {bool} [props.underlineOnHover] - if the button has underline on hover
|
|
*/
|
|
class Button extends React.PureComponent {
|
|
|
|
state = {
|
|
isHovering: false,
|
|
}
|
|
|
|
handleClick = (e) => {
|
|
if (!this.props.isDisabled && this.props.onClick) {
|
|
this.props.onClick(e)
|
|
}
|
|
}
|
|
|
|
handleOnMouseEnter = () => {
|
|
if (!this.props.isDisabled && this.props.onMouseEnter) {
|
|
this.props.onMouseEnter()
|
|
}
|
|
if (this.props.tooltip) {
|
|
this.setState({ isHovering: true })
|
|
}
|
|
}
|
|
|
|
handleOnMouseLeave = () => {
|
|
if (!this.props.isDisabled && this.props.onMouseLeave) {
|
|
this.props.onMouseLeave()
|
|
}
|
|
if (this.props.tooltip) {
|
|
this.setState({ isHovering: false })
|
|
}
|
|
}
|
|
|
|
setRef = (c) => {
|
|
try {
|
|
this.node = c
|
|
this.props.buttonRef(c)
|
|
} catch (error) {
|
|
//
|
|
}
|
|
}
|
|
|
|
focus() {
|
|
this.node.focus()
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
backgroundColor,
|
|
children,
|
|
className,
|
|
color,
|
|
href,
|
|
icon,
|
|
iconClassName,
|
|
iconSize,
|
|
isBlock,
|
|
isDisabled,
|
|
isNarrow,
|
|
isOutline,
|
|
isText,
|
|
noClasses,
|
|
onClick,
|
|
onMouseEnter,
|
|
onMouseLeave,
|
|
radiusSmall,
|
|
rel,
|
|
target,
|
|
title,
|
|
to,
|
|
tooltip,
|
|
type,
|
|
underlineOnHover,
|
|
} = this.props
|
|
const { isHovering } = this.state
|
|
|
|
// Style the component according to props
|
|
const classes = noClasses ? className : CX(className, {
|
|
d: 1,
|
|
noUnderline: 1,
|
|
font: 1,
|
|
cursorPointer: 1,
|
|
textAlignCenter: 1,
|
|
outlineNone: 1,
|
|
// outlineOnFocus: !isText,
|
|
flexRow: !!children && !!icon,
|
|
cursorNotAllowed: isDisabled,
|
|
opacity05: isDisabled,
|
|
|
|
bgPrimary: backgroundColor === COLORS.primary,
|
|
bgWhite: backgroundColor === COLORS.white,
|
|
bgBlack: backgroundColor === COLORS.black,
|
|
bgBrand: backgroundColor === COLORS.brand,
|
|
bgTransparent: backgroundColor === COLORS.none,
|
|
bgSecondary: backgroundColor === COLORS.tertiary,
|
|
bgSubtle: backgroundColor === COLORS.secondary,
|
|
bgDanger: backgroundColor === COLORS.danger,
|
|
|
|
cPrimary: color === COLORS.primary,
|
|
cSecondary: color === COLORS.secondary,
|
|
cTertiary: color === COLORS.tertiary,
|
|
cWhite: color === COLORS.white,
|
|
cBrand: color === COLORS.brand,
|
|
|
|
borderColorBrand: color === COLORS.brand && isOutline,
|
|
border1PX: isOutline,
|
|
|
|
circle: !isText,
|
|
radiusSmall: radiusSmall,
|
|
|
|
py5: isNarrow,
|
|
py10: !isText && !isNarrow,
|
|
px15: !isText,
|
|
|
|
w100PC: isBlock,
|
|
|
|
underline_onHover: underlineOnHover,
|
|
|
|
bgSecondaryDark_onHover: backgroundColor === COLORS.tertiary || backgroundColor === COLORS.secondary && !isDisabled,
|
|
bgBlackOpaque_onHover: backgroundColor === COLORS.black && !isDisabled,
|
|
bgBrandDark_onHover: backgroundColor === COLORS.brand && !isDisabled,
|
|
bgDangerDark_onHover: backgroundColor === COLORS.danger && !isDisabled,
|
|
|
|
bgBrand_onHover: color === COLORS.brand && isOutline && !isDisabled,
|
|
cWhite_onHover: color === COLORS.brand && isOutline && !isDisabled,
|
|
})
|
|
|
|
const tagName = !!href ? 'a' : !!to ? 'NavLink' : 'button'
|
|
|
|
const theIcon = !!icon ? (
|
|
<Icon
|
|
id={icon}
|
|
size={iconSize}
|
|
className={iconClassName}
|
|
/>
|
|
) : undefined
|
|
|
|
const theTooltip = !!tooltip && isHovering ? (
|
|
<Tooltip
|
|
message={tooltip}
|
|
targetRef={this.node}
|
|
/>
|
|
) : undefined
|
|
|
|
const theChildren = !!icon || !!tooltip ? (
|
|
<React.Fragment>
|
|
{theIcon}
|
|
{children}
|
|
{theTooltip}
|
|
</React.Fragment>
|
|
) : children
|
|
|
|
const handlers = {
|
|
onClick: !!onClick ? this.handleClick : undefined,
|
|
onMouseEnter: !!onMouseEnter || !!tooltip ? this.handleOnMouseEnter : undefined,
|
|
onMouseLeave: !!onMouseLeave || !!tooltip ? this.handleOnMouseLeave : undefined,
|
|
}
|
|
|
|
if (tagName === 'NavLink' && !!to) {
|
|
return (
|
|
<NavLink
|
|
title={title}
|
|
className={classes}
|
|
disabled={isDisabled}
|
|
to={to}
|
|
{...handlers}
|
|
>
|
|
{theChildren}
|
|
</NavLink>
|
|
)
|
|
}
|
|
|
|
const isLogout = href === '/auth/sign_out'
|
|
const dataMethod = isLogout ? 'delete' : undefined
|
|
|
|
const options = {
|
|
rel,
|
|
target,
|
|
title,
|
|
'aria-label': title,
|
|
type,
|
|
disabled: isDisabled,
|
|
className: classes,
|
|
href: href || undefined,
|
|
ref: this.setRef,
|
|
'data-method': dataMethod,
|
|
...handlers,
|
|
}
|
|
|
|
return React.createElement(tagName, options, theChildren)
|
|
}
|
|
|
|
}
|
|
|
|
Button.propTypes = {
|
|
backgroundColor: PropTypes.string,
|
|
buttonRef: PropTypes.oneOfType([
|
|
PropTypes.func,
|
|
PropTypes.node,
|
|
]),
|
|
children: PropTypes.node,
|
|
className: PropTypes.string,
|
|
color: PropTypes.string,
|
|
href: PropTypes.string,
|
|
icon: PropTypes.string,
|
|
iconClassName: PropTypes.string,
|
|
iconSize: PropTypes.string,
|
|
isBlock: PropTypes.bool,
|
|
isDisabled: PropTypes.bool,
|
|
isNarrow: PropTypes.bool,
|
|
isText: PropTypes.bool,
|
|
noClasses: PropTypes.bool,
|
|
onClick: PropTypes.func,
|
|
onMouseEnter: PropTypes.func,
|
|
onMouseLeave: PropTypes.func,
|
|
isOutline: PropTypes.bool,
|
|
radiusSmall: PropTypes.bool,
|
|
rel: PropTypes.string,
|
|
target: PropTypes.string,
|
|
title: PropTypes.string,
|
|
to: PropTypes.string,
|
|
type: PropTypes.string,
|
|
underlineOnHover: PropTypes.bool,
|
|
}
|
|
|
|
Button.defaultProps = {
|
|
color: COLORS.white,
|
|
backgroundColor: COLORS.brand,
|
|
}
|
|
|
|
export default Button
|