lucid-ui
Version:
A UI component library from AppNexus.
122 lines (121 loc) • 5.49 kB
JavaScript
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 };