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 ? ( ) : undefined const theTooltip = !!tooltip && isHovering ? ( ) : undefined const theChildren = !!icon || !!tooltip ? ( {theIcon} {children} {theTooltip} ) : 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 ( {theChildren} ) } 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