UNPKG

@salesforce/design-system-react

Version:

Salesforce Lightning Design System for React

273 lines (252 loc) 7.15 kB
/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */ /* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */ // Implements the [Button Stateful design pattern](https://lightningdesignsystem.com/components/buttons/#flavor-stateful) in React. // Based on SLDS v2.1.1 // ## Dependencies // ### React import React from 'react'; import PropTypes from 'prop-types'; // ### classNames import classNames from 'classnames'; // ### isBoolean import isBoolean from 'lodash.isboolean'; // ### isFunction import isFunction from 'lodash.isfunction'; // ## Children import ButtonIcon from '../icon/button-icon'; import { BUTTON_STATEFUL } from '../../utilities/constants'; const propTypes = { /** * Specifies the current state of the button. If set, the button will act as a ['controlled' component](https://facebook.github.io/react/docs/forms.html#controlled-components). */ active: PropTypes.bool, /** * Text that is visually hidden but read aloud by screenreaders to tell the user what the icon means. This should also include the state of the button. * If the button has an icon and a visible label, you can omit the <code>assistiveText</code> prop and use the <code>label</code> prop. */ assistiveText: PropTypes.string, /** * Disables the button and adds disabled styling. */ disabled: PropTypes.bool, /** * Name of the icon. Visit <a href='http://www.lightningdesignsystem.com/resources/icons'>Lightning Design System Icons</a> to reference icon names. */ iconName: PropTypes.string, /** * Determines the size of the icon. */ iconSize: PropTypes.oneOf(['x-small', 'small', 'medium', 'large']), /** * If true, button/icon is white. Meant for buttons or utility icons on dark backgrounds. */ inverse: PropTypes.bool, /** * Triggered when focus is removed. */ onBlur: PropTypes.func, /** * Triggered when the button is clicked. */ onClick: PropTypes.func, /** * Triggered when component is focused. */ onFocus: PropTypes.func, /** * Triggered when a key is pressed down */ onKeyDown: PropTypes.func, /** * Triggered when a key is pressed and released */ onKeyPress: PropTypes.func, /** * Triggered when a key is released */ onKeyUp: PropTypes.func, /** * Triggered when a mouse button is pressed down */ onMouseDown: PropTypes.func, /** * Triggered when a mouse arrow hovers */ onMouseEnter: PropTypes.func, /** * Triggered when a mouse arrow no longer hovers */ onMouseLeave: PropTypes.func, /** * If true, button scales to 100% width on small form factors. */ responsive: PropTypes.bool, /** * Initial label and icon (optional) of button. */ stateOne: PropTypes.object, /** * Selected label and icon (optional) of button. */ stateTwo: PropTypes.object, /** * Deselect label and icon (optional) of button. */ stateThree: PropTypes.object, /** * Write "-1" if you don't want the user to tab to the button. */ tabIndex: PropTypes.string, /** * [Deprecated] Tooltip on button. Button should be a child of `Tooltip` instead. */ tooltip: PropTypes.node, /** * Different types of buttons */ variant: PropTypes.oneOf(['base', 'neutral', 'brand', 'destructive', 'icon']), }; // i18n const defaultProps = { disabled: false, iconSize: 'medium', responsive: false, stateOne: { iconName: 'add', label: 'Follow' }, stateTwo: { iconName: 'check', label: 'Following' }, stateThree: { iconName: 'close', label: 'Unfollow' }, }; /** * The ButtonStateful component is a variant of the Lightning Design System Button component. It is used for buttons that have a state of unselected or selected. * For icon buttons, use <code>variant='icon'</code>. For buttons with labels or buttons with labels and icons, pass data to the state props (ie. <code>stateOne={{iconName: 'add', label: 'Join'}}</code>). */ class ButtonStateful extends React.Component { constructor (props) { super(props); this.state = { active: false }; } getClassName (active) { return classNames(this.props.className, 'slds-button', { 'slds-button--neutral': this.props.variant !== 'icon', 'slds-button--inverse': this.props.variant === 'inverse', 'slds-not-selected': !active, 'slds-is-selected': active, 'slds-max-small-button--stretch': this.props.responsive, 'slds-button--icon-border': this.props.variant === 'icon', }); } handleBlur = (e) => { if (this.props.onBlur) this.props.onBlur(e); e.currentTarget.blur(); }; handleClick = (e) => { if (isFunction(this.props.onClick)) this.props.onClick(e); if (!isBoolean(this.props.active)) { this.setState({ active: !this.state.active }); } }; render () { const { active, assistiveText, disabled, iconName, iconSize, id, onFocus, onKeyDown, onKeyPress, onKeyUp, onMouseDown, onMouseEnter, // onMouseLeave, // This prop isn't used anywhere! But removing it would be a breaking change stateOne, stateTwo, stateThree, tabIndex, variant, } = this.props; const isActive = isBoolean(active) ? active : this.state.active; if (variant === 'icon') { return ( <button aria-live="polite" className={this.getClassName(isActive)} disabled={disabled} id={id} onBlur={this.handleBlur} onClick={this.handleClick} onFocus={onFocus} onKeyDown={onKeyDown} onKeyPress={onKeyPress} onKeyUp={onKeyUp} onMouseDown={onMouseDown} onMouseEnter={onMouseEnter} onMouseLeave={this.handleBlur} tabIndex={tabIndex} > <ButtonIcon disabled={disabled} name={iconName} size={iconSize} className="slds-button__icon--stateful" /> {assistiveText ? ( <span className="slds-assistive-text">{assistiveText}</span> ) : null} </button> ); } return ( <button aria-live="assertive" className={this.getClassName(isActive)} disabled={disabled} id={id} onBlur={this.handleBlur} onClick={this.handleClick} onFocus={onFocus} onKeyDown={onKeyDown} onKeyPress={onKeyPress} onKeyUp={onKeyUp} onMouseEnter={onMouseEnter} onMouseLeave={this.handleBlur} tabIndex={tabIndex} > <span className="slds-text-not-selected"> <ButtonIcon disabled={disabled} name={stateOne.iconName} size="small" position="left" className="slds-button__icon--stateful" /> {stateOne.label} </span> <span className="slds-text-selected"> <ButtonIcon disabled={disabled} name={stateTwo.iconName} size="small" position="left" className="slds-button__icon--stateful" /> {stateTwo.label} </span> <span className="slds-text-selected-focus"> <ButtonIcon disabled={disabled} name={stateThree.iconName} size="small" position="left" className="slds-button__icon--stateful" /> {stateThree.label} </span> </button> ); } } ButtonStateful.displayName = BUTTON_STATEFUL; ButtonStateful.propTypes = propTypes; ButtonStateful.defaultProps = defaultProps; export default ButtonStateful;