UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

89 lines (88 loc) 3.95 kB
import _ from 'lodash'; import Button from '../Button/Button'; import React from 'react'; import PropTypes from 'react-peek/prop-types'; import { lucidClassNames } from '../../util/style-helpers'; import { findTypes, omitProps, } from '../../util/component-types'; import reducers from './ButtonGroup.reducers'; import { buildModernHybridComponent } from '../../util/state-management'; const cx = lucidClassNames.bind('&-ButtonGroup'); const { any, func, arrayOf, number } = PropTypes; const ButtonGroupButton = (_props) => null; ButtonGroupButton.displayName = 'ButtonGroup.Button'; ButtonGroupButton.peek = { description: ` Renders a \`<Button\`> inside the \`ButtonGroup\`. `, }; const defaultProps = { onSelect: _.noop, selectedIndices: [], }; class ButtonGroup extends React.Component { constructor() { super(...arguments); this.handleSelect = ({ event, props: childProps, }) => { const { callbackId } = childProps; const clickedButtonProps = _.get(findTypes(this.props, ButtonGroup.Button)[callbackId], 'props', {}); // If the consumer passed in an `onClick` to the child `ButtonGroup.Button` // component, we should make sure to call that in addition to the // `ButtonGroup`'s `onSelect`. if (_.isFunction(clickedButtonProps.onClick)) { clickedButtonProps.onClick({ event, props: childProps }); } this.props.onSelect(callbackId, { event, props: childProps }); }; } render() { const { selectedIndices, className, children, ...passThroughs } = this.props; const buttonChildProps = _.map(findTypes(this.props, ButtonGroup.Button), 'props'); return (React.createElement("span", Object.assign({}, omitProps(passThroughs, undefined, _.keys(ButtonGroup.propTypes)), { className: cx('&', className) }), _.map(buttonChildProps, (buttonChildProp, index) => { return ( // The order of the spread operator below is important. If the // consumer puts `isActive` directly on a `ButtonGroup.Button`, we // want that to take precedence over the `selectedIndices` prop on // the parent `ButtonGroup`. However, we want our `onClick` at the // bottom because we manually handle passing the event to the // `ButtonGroup.Button`'s `onClick` if it exists. React.createElement(Button, Object.assign({ isActive: _.includes(selectedIndices, index) }, buttonChildProp, { key: index, callbackId: index, onClick: this.handleSelect }))); }), children)); } } ButtonGroup.displayName = 'ButtonGroup'; ButtonGroup.peek = { description: ` Button groups allow you to pair buttons together to form a seamless cluster. Any props not explicitly called out are spread on to the root component. `, categories: ['controls', 'buttons'], madeFrom: ['Button'], }; ButtonGroup.Button = ButtonGroupButton; ButtonGroup.reducers = reducers; ButtonGroup.defaultProps = defaultProps; ButtonGroup.propTypes = { onSelect: func ` A function that is called with the index of the child button clicked. \`props\` refers to the child button props. Signature: \`(selectedIndex, { event, props }) => {}\` `, className: any ` Appended to the component-specific class names set on the root element. Value is run through the \`classnames\` library. `, children: any ` All children should be \`ButtonGroup.Button\`s and they support the same props as \`Button\`s. `, selectedIndices: arrayOf(number) ` An array of currently selected \`ButtonGroup.Button\`s indices. You can also pass the prop \`isActive\` to individual \`ButtonGroup.Button\` components. `, }; export default buildModernHybridComponent(ButtonGroup, { reducers }); export { ButtonGroup as ButtonGroupDumb };