1
1
import cx from 'classnames'
2
2
import _ from 'lodash'
3
3
import PropTypes from 'prop-types'
4
- import React from 'react'
4
+ import React , { createRef } from 'react'
5
5
6
6
import {
7
7
AutoControlledComponent as Component ,
@@ -117,6 +117,9 @@ export default class Checkbox extends Component {
117
117
118
118
static autoControlledProps = [ 'checked' , 'indeterminate' ]
119
119
120
+ inputRef = createRef ( )
121
+ labelRef = createRef ( )
122
+
120
123
componentDidMount ( ) {
121
124
this . setIndeterminate ( )
122
125
}
@@ -139,29 +142,50 @@ export default class Checkbox extends Component {
139
142
return disabled ? - 1 : 0
140
143
}
141
144
142
- handleInputRef = c => ( this . inputRef = c )
143
-
144
- handleChange = ( e , fromMouseUp ) => {
145
- debug ( 'handleChange()' )
145
+ handleClick = ( e ) => {
146
+ debug ( 'handleClick()' , _ . get ( e , 'target.tagName' ) )
146
147
const { id } = this . props
147
148
const { checked, indeterminate } = this . state
148
149
150
+ const hasId = ! _ . isNil ( id )
151
+ const isLabelClick = e . target === this . labelRef . current
152
+ const isLabelClickAndForwardedToInput = isLabelClick && hasId
153
+
154
+ // https://github.com/Semantic-Org/Semantic-UI-React/pull/3351
155
+ if ( ! isLabelClickAndForwardedToInput ) {
156
+ _ . invoke ( this . props , 'onClick' , e , {
157
+ ...this . props ,
158
+ checked : ! checked ,
159
+ indeterminate : ! ! indeterminate ,
160
+ } )
161
+ }
162
+
163
+ if ( this . isClickFromMouse ) {
164
+ this . isClickFromMouse = false
165
+
166
+ if ( isLabelClick && ! hasId ) {
167
+ this . handleChange ( e )
168
+ }
169
+
170
+ if ( hasId ) {
171
+ // To prevent two clicks from being fired from the component we have to stop the propagation
172
+ // from the "input" click: https://github.com/Semantic-Org/Semantic-UI-React/issues/3433
173
+ e . stopPropagation ( )
174
+ }
175
+ }
176
+ }
177
+
178
+ handleChange = ( e ) => {
179
+ const { checked } = this . state
180
+
149
181
if ( ! this . canToggle ( ) ) return
150
- if ( fromMouseUp && ! _ . isNil ( id ) ) return
182
+ debug ( 'handleChange()' , _ . get ( e , 'target.tagName' ) )
151
183
152
- // We don't have a separate click handler as it's already called in here,
153
- // and also to avoid duplicate calls, matching all DOM Checkbox comparisons.
154
- _ . invoke ( this . props , 'onClick' , e , {
155
- ...this . props ,
156
- checked : ! checked ,
157
- indeterminate : ! ! indeterminate ,
158
- } )
159
184
_ . invoke ( this . props , 'onChange' , e , {
160
185
...this . props ,
161
186
checked : ! checked ,
162
187
indeterminate : false ,
163
188
} )
164
-
165
189
this . trySetState ( { checked : ! checked , indeterminate : false } )
166
190
}
167
191
@@ -174,24 +198,23 @@ export default class Checkbox extends Component {
174
198
checked : ! ! checked ,
175
199
indeterminate : ! ! indeterminate ,
176
200
} )
177
- _ . invoke ( this . inputRef , 'focus' )
178
201
202
+ _ . invoke ( this . inputRef . current , 'focus' )
203
+ // Heads up!
204
+ // We need to call "preventDefault" to keep element focused.
179
205
e . preventDefault ( )
180
206
}
181
207
182
208
handleMouseUp = ( e ) => {
183
209
debug ( 'handleMouseUp()' )
184
210
const { checked, indeterminate } = this . state
185
211
212
+ this . isClickFromMouse = true
186
213
_ . invoke ( this . props , 'onMouseUp' , e , {
187
214
...this . props ,
188
215
checked : ! ! checked ,
189
216
indeterminate : ! ! indeterminate ,
190
217
} )
191
-
192
- // Handle mouseUp only on the left mouse button.
193
- // https://github.com/Semantic-Org/Semantic-UI-React/issues/3419
194
- if ( e . button === 0 ) this . handleChange ( e , true )
195
218
}
196
219
197
220
// Note: You can't directly set the indeterminate prop on the input, so we
@@ -200,7 +223,7 @@ export default class Checkbox extends Component {
200
223
setIndeterminate = ( ) => {
201
224
const { indeterminate } = this . state
202
225
203
- if ( this . inputRef ) this . inputRef . indeterminate = ! ! indeterminate
226
+ _ . set ( this . inputRef , 'current. indeterminate' , ! ! indeterminate )
204
227
}
205
228
206
229
render ( ) {
@@ -242,6 +265,7 @@ export default class Checkbox extends Component {
242
265
< ElementType
243
266
{ ...rest }
244
267
className = { classes }
268
+ onClick = { this . handleClick }
245
269
onChange = { this . handleChange }
246
270
onMouseDown = { this . handleMouseDown }
247
271
onMouseUp = { this . handleMouseUp }
@@ -254,7 +278,7 @@ export default class Checkbox extends Component {
254
278
id = { id }
255
279
name = { name }
256
280
readOnly
257
- ref = { this . handleInputRef }
281
+ ref = { this . inputRef }
258
282
tabIndex = { this . computeTabIndex ( ) }
259
283
type = { type }
260
284
value = { value }
@@ -263,9 +287,10 @@ export default class Checkbox extends Component {
263
287
Heads Up!
264
288
Do not remove empty labels, they are required by SUI CSS
265
289
*/ }
266
- { createHTMLLabel ( label , { defaultProps : { htmlFor : id } , autoGenerateKey : false } ) || (
267
- < label htmlFor = { id } />
268
- ) }
290
+ { createHTMLLabel ( label , {
291
+ defaultProps : { htmlFor : id , ref : this . labelRef } ,
292
+ autoGenerateKey : false ,
293
+ } ) || < label htmlFor = { id } ref = { this . labelRef } /> }
269
294
</ ElementType >
270
295
)
271
296
}
0 commit comments