| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 |
134x
134x
134x
268x
2x
2x
2x
1x
2x
10x
10x
10x
21x
| import _ from 'lodash';
import Button from '../Button/Button';
import React from 'react';
import PropTypes from 'prop-types';
import { lucidClassNames } from '../../util/style-helpers';
import { createClass, findTypes, omitProps } from '../../util/component-types';
import reducers from './ButtonGroup.reducers';
import { buildHybridComponent } from '../../util/state-management';
const cx = lucidClassNames.bind('&-ButtonGroup');
const { any, func, arrayOf, number } = PropTypes;
/**
*
* {"categories": ["controls", "buttons"], "madeFrom": ["Button"]}
*
* 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.
*/
const ButtonGroup = createClass({
displayName: 'ButtonGroup',
components: {
/**
* Renders a `<Button`> inside the `ButtonGroup`.
*/
Button: createClass({
displayName: 'ButtonGroup.Button',
}),
},
reducers: reducers,
propTypes: {
/**
* A function that is called with the index of the child button clicked.
* `props` refers to the child button props.
*
* Signature: `(selectedIndex, { event, props }) => {}`
*/
onSelect: func,
/**
* Appended to the component-specific class names set on the root
* element. Value is run through the `classnames` library.
*/
className: any,
/**
* All children should be `ButtonGroup.Button`s and they support the same
* props as `Button`s.
*/
children: any,
/**
* An array of currently selected `ButtonGroup.Button`s indices. You can
* also pass the prop `isActive` to individual `ButtonGroup.Button`
* components.
*/
selectedIndices: arrayOf(number),
},
getDefaultProps() {
return {
onSelect: _.noop,
className: null,
children: null,
selectedIndices: [],
};
},
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 (
<span
{...omitProps(passThroughs, ButtonGroup)}
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.
(
<Button
isActive={_.includes(selectedIndices, index)}
{...buttonChildProp}
key={index}
callbackId={index}
onClick={this.handleSelect}
/>
)
);
})}
{children}
</span>
);
},
});
export default buildHybridComponent(ButtonGroup);
export { ButtonGroup as ButtonGroupDumb };
|