UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

122 lines (121 loc) 5.49 kB
import _ from 'lodash'; import React from 'react'; import PropTypes from 'react-peek/prop-types'; import { lucidClassNames } from '../../util/style-helpers'; import { filterTypes, getFirst, omitProps, } from '../../util/component-types'; import { buildModernHybridComponent } from '../../util/state-management'; import { Button } from '../Button/Button'; import { ButtonGroupDumb as ButtonGroup } from '../ButtonGroup/ButtonGroup'; import ChevronIcon from '../Icon/ChevronIcon/ChevronIcon'; import { DropMenuDumb as DropMenu, } from '../DropMenu/DropMenu'; import * as reducers from './SplitButton.reducers'; const cx = lucidClassNames.bind('&-SplitButton'); const { bool, func, node, oneOf, shape, string } = PropTypes; const ButtonChild = (_props) => null; ButtonChild.displayName = 'SplitButton.ButtonChild'; ButtonChild.peek = { description: ` One of many potential \`Button\`s to render in this \`SplitButton\`. The first \`Button\` will be used as the Primary button, while all others will be rendered within the \`DropMenu\` below. `, }; ButtonChild.propTypes = { children: node ` The children to render within the \`Button\`. `, isDisabled: bool ` Disables selection of the \`Button\`. `, onClick: func ` Called when the user clicks the \`Button\`. Signature: \`({ props, event }) => {}\` `, }; class SplitButton extends React.Component { constructor() { super(...arguments); // Handles select events in the DropMenu this.handleSelect = (optionIndex, { event, }) => { const buttonChildProps = _.map(filterTypes(this.props.children, SplitButton.Button), 'props'); if (optionIndex !== null) { this.handleButtonClick(buttonChildProps[optionIndex + 1], event); } }; // Handles clicks on the Primary Button this.handleClick = ({ event, }) => { const clickedButtonProps = _.get(getFirst(this.props, SplitButton.Button), 'props'); // Stop propagation to prevent this `Click` from expanding the `DropMenu` event.stopPropagation(); this.handleButtonClick(clickedButtonProps, event); }; // Handles clicks within handleClick and handleSelect this.handleButtonClick = (buttonProps, event) => { const { DropMenu: { onCollapse }, } = this.props; onCollapse && onCollapse({ props: this.props.DropMenu, event }); if (_.has(buttonProps, 'onClick')) { buttonProps.onClick({ event, props: buttonProps }); } }; } render() { const { className, kind, direction, type, size, DropMenu: dropMenuProps, ...passThroughs } = this.props; const { isExpanded } = dropMenuProps; const [primaryButtonProps, ...buttonChildProps] = _.map(filterTypes(this.props.children, SplitButton.Button), 'props'); return (React.createElement(DropMenu, Object.assign({}, dropMenuProps, omitProps(passThroughs, undefined, _.keys(DropMenu.Option.propTypes)), { direction: direction, className: cx('&', className), onSelect: this.handleSelect }), React.createElement(DropMenu.Control, null, React.createElement(ButtonGroup, null, React.createElement(Button, Object.assign({}, primaryButtonProps, { className: cx('&-Button-primary', _.get(primaryButtonProps, 'className')), kind: kind, type: type, size: size, onClick: this.handleClick })), React.createElement(Button, { className: cx('&-Button-drop'), size: size, hasOnlyIcon: true, isActive: isExpanded, kind: kind, isDisabled: _.every([primaryButtonProps, ...buttonChildProps], 'isDisabled') }, React.createElement(ChevronIcon, { className: cx('&-ChevronIcon'), direction: direction, size: 10 })))), _.map(buttonChildProps, (buttonChildProp, index) => (React.createElement(DropMenu.Option, Object.assign({}, buttonChildProp, { key: index })))))); } } SplitButton.displayName = 'SplitButton'; SplitButton.Button = ButtonChild; SplitButton.peek = { description: ` \`SplitButton\` allow you to combine a single main \`Button\` together with a list of additional \`Buttons\` with actions which will be rendered within a \`DropMenu\`. `, categories: ['controls', 'buttons'], madeFrom: ['Button', 'DropMenu'], }; SplitButton.reducers = reducers; SplitButton.propTypes = { DropMenu: shape(DropMenu.propTypes) ` Object of DropMenu props which are passed through to the underlying DropMenu component. `, children: node ` All children should be \`ButtonGroup.Button\`s and they support the same props as \`Button\`s. `, className: string ` Appended to the component-specific class names set on the root element. Value is run through the \`classnames\` library. `, direction: oneOf(['up', 'down']) ` Sets the direction the flyout menu will render relative to the SplitButton. `, kind: oneOf(['primary', 'danger']) ` Style variations of the SplitButton. `, size: oneOf(['short', 'small', 'large']) ` Size variations of the SplitButton. `, type: string ` Form element type variations of SplitButton. Passed through to DOM Button. `, }; SplitButton.defaultProps = { direction: 'down', type: 'button', DropMenu: DropMenu.defaultProps, }; export default buildModernHybridComponent(SplitButton, { reducers }); export { SplitButton as SplitButtonDumb };