| 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 |
120x
120x
120x
120x
51x
9x
64x
64x
34x
34x
24x
24x
| import _ from 'lodash';
import React from 'react';
import { lucidClassNames } from '../../util/style-helpers';
import { createClass, omitProps } from '../../util/component-types';
const cx = lucidClassNames.bind('&-RadioButton');
const {
bool,
func,
object,
string,
} = React.PropTypes;
/**
* {"categories": ["controls", "toggles"]}
*
* This is a toggle -- a component that is in one of two particular states at
* any given moment in time -- that features a custom visualization of the
* native radio button control to reflect its current state.
*
* The radio button is different from a standard toggle in that when it is in
* the selected state user events do not cause it to toggle to the unselected
* state so the `onSelect` function is called only when `isSelected` is false.
*
* It uses a hidden native radio button control under the hood but leverages
* other HTML elements to visualize its state.
*
* Any props that are not explicitly defined in `propTypes` are spread onto the
* native control.
*/
const RadioButton = createClass({
displayName: 'RadioButton',
propTypes: {
/**
* Appended to the component-specific class names set on the root
* element.
*/
className: string,
/**
* Indicates whether the component should appear and act disabled by
* having a "greyed out" palette and ignoring user interactions.
*/
isDisabled: bool,
/**
* Indicates that the component is in the "selected" state when true
* and in the "unselected" state when false.
*/
isSelected: bool,
/**
* Called when the user clicks on the component or when they press the
* space key while the component is in focus, and only called when the
* component is in the unselected state.
*
* Signature: `(true, { event, props }) => {}`
*/
onSelect: func,
/**
* Passed through to the root element.
*/
style: object,
},
getDefaultProps() {
return {
isDisabled: false,
isSelected: false,
onSelect: _.noop,
};
},
componentDidMount() {
this.nativeElement = this.refs.nativeElement;
},
handleSpanClick(e) {
e.preventDefault();
},
render() {
const {
className,
isDisabled,
isSelected,
style,
...passThroughs,
} = this.props;
return (
<span
className={cx('&', {
'&-is-disabled': isDisabled,
'&-is-selected': isSelected,
}, className)}
onClick={this.handleClicked}
onTouchEnd={this.handleClicked}
style={style}
>
<input
onChange={_.noop}
{...omitProps(passThroughs, RadioButton, ['children', 'callbackId'])}
checked={isSelected}
className={cx('&-native')}
disabled={isDisabled}
ref='nativeElement'
type='radio'
/>
<span onClick={this.handleSpanClick} className={cx('&-visualization-glow')} />
<span onClick={this.handleSpanClick} className={cx('&-visualization-container')} />
<span onClick={this.handleSpanClick} className={cx('&-visualization-dot')} />
</span>
);
},
handleClicked(event) {
const {
isDisabled,
isSelected,
onSelect,
} = this.props;
if (!isDisabled && !isSelected) {
onSelect(true, { event, props: this.props });
this.nativeElement.focus();
}
},
});
export default RadioButton;
|