1
- import { Ref } from '@fluentui/react-component-ref'
2
1
import cx from 'clsx'
3
2
import _ from 'lodash'
4
3
import PropTypes from 'prop-types'
5
- import React , { Component , createRef } from 'react'
4
+ import React from 'react'
6
5
7
6
import {
8
7
childrenUtils ,
@@ -14,6 +13,7 @@ import {
14
13
useKeyOnly ,
15
14
useKeyOrValueAndKey ,
16
15
useValueAndKey ,
16
+ useMergedRefs ,
17
17
} from '../../lib'
18
18
import Icon from '../Icon/Icon'
19
19
import Label from '../Label/Label'
@@ -22,165 +22,176 @@ import ButtonGroup from './ButtonGroup'
22
22
import ButtonOr from './ButtonOr'
23
23
24
24
/**
25
- * A Button indicates a possible user action.
26
- * @see Form
27
- * @see Icon
28
- * @see Label
25
+ * @param {React.ElementType } ElementType
26
+ * @param {String } role
29
27
*/
30
- class Button extends Component {
31
- ref = createRef ( )
32
-
33
- computeButtonAriaRole ( ElementType ) {
34
- const { role } = this . props
35
-
36
- if ( ! _ . isNil ( role ) ) return role
37
- if ( ElementType !== 'button' ) return 'button'
28
+ function computeButtonAriaRole ( ElementType , role ) {
29
+ if ( ! _ . isNil ( role ) ) {
30
+ return role
38
31
}
39
32
40
- computeElementType = ( ) => {
41
- const { attached, label } = this . props
33
+ if ( ElementType !== 'button' ) {
34
+ return 'button'
35
+ }
36
+ }
42
37
43
- if ( ! _ . isNil ( attached ) || ! _ . isNil ( label ) ) return 'div'
38
+ /**
39
+ * @param {React.ElementType } ElementType
40
+ * @param {Boolean } disabled
41
+ * @param {Number } tabIndex
42
+ */
43
+ function computeTabIndex ( ElementType , disabled , tabIndex ) {
44
+ if ( ! _ . isNil ( tabIndex ) ) {
45
+ return tabIndex
46
+ }
47
+ if ( disabled ) {
48
+ return - 1
49
+ }
50
+ if ( ElementType === 'div' ) {
51
+ return 0
44
52
}
53
+ }
45
54
46
- computeTabIndex = ( ElementType ) => {
47
- const { disabled , tabIndex } = this . props
55
+ function hasIconClass ( props ) {
56
+ const { children , content , icon , labelPosition } = props
48
57
49
- if ( ! _ . isNil ( tabIndex ) ) return tabIndex
50
- if ( disabled ) return - 1
51
- if ( ElementType === 'div' ) return 0
58
+ if ( icon === true ) {
59
+ return true
52
60
}
53
61
54
- focus = ( ) => _ . invoke ( this . ref . current , 'focus' )
62
+ if ( icon ) {
63
+ return labelPosition || ( childrenUtils . isNil ( children ) && _ . isNil ( content ) )
64
+ }
65
+ }
55
66
56
- handleClick = ( e ) => {
57
- const { disabled } = this . props
67
+ /**
68
+ * A Button indicates a possible user action.
69
+ * @see Form
70
+ * @see Icon
71
+ * @see Label
72
+ */
73
+ const Button = React . forwardRef ( function ( props , ref ) {
74
+ const {
75
+ active,
76
+ animated,
77
+ attached,
78
+ basic,
79
+ children,
80
+ circular,
81
+ className,
82
+ color,
83
+ compact,
84
+ content,
85
+ disabled,
86
+ floated,
87
+ fluid,
88
+ icon,
89
+ inverted,
90
+ label,
91
+ labelPosition,
92
+ loading,
93
+ negative,
94
+ positive,
95
+ primary,
96
+ secondary,
97
+ size,
98
+ toggle,
99
+ type
100
+ } = props
101
+ const elementRef = useMergedRefs ( ref , React . useRef ( ) )
102
+
103
+ const baseClasses = cx (
104
+ color ,
105
+ size ,
106
+ useKeyOnly ( active , 'active' ) ,
107
+ useKeyOnly ( basic , 'basic' ) ,
108
+ useKeyOnly ( circular , 'circular' ) ,
109
+ useKeyOnly ( compact , 'compact' ) ,
110
+ useKeyOnly ( fluid , 'fluid' ) ,
111
+ useKeyOnly ( hasIconClass ( props ) , 'icon' ) ,
112
+ useKeyOnly ( inverted , 'inverted' ) ,
113
+ useKeyOnly ( loading , 'loading' ) ,
114
+ useKeyOnly ( negative , 'negative' ) ,
115
+ useKeyOnly ( positive , 'positive' ) ,
116
+ useKeyOnly ( primary , 'primary' ) ,
117
+ useKeyOnly ( secondary , 'secondary' ) ,
118
+ useKeyOnly ( toggle , 'toggle' ) ,
119
+ useKeyOrValueAndKey ( animated , 'animated' ) ,
120
+ useKeyOrValueAndKey ( attached , 'attached' ) ,
121
+ )
122
+ const labeledClasses = cx ( useKeyOrValueAndKey ( labelPosition || ! ! label , 'labeled' ) )
123
+ const wrapperClasses = cx ( useKeyOnly ( disabled , 'disabled' ) , useValueAndKey ( floated , 'floated' ) )
124
+
125
+ const rest = getUnhandledProps ( Button , props )
126
+ const ElementType = getElementType ( Button , props , ( ) => {
127
+ if ( ! _ . isNil ( attached ) || ! _ . isNil ( label ) ) {
128
+ return 'div'
129
+ }
130
+ } )
131
+ const tabIndex = computeTabIndex ( ElementType , disabled , props . tabIndex )
58
132
133
+ const handleClick = ( e ) => {
59
134
if ( disabled ) {
60
135
e . preventDefault ( )
61
136
return
62
137
}
63
138
64
- _ . invoke ( this . props , 'onClick' , e , this . props )
65
- }
66
-
67
- hasIconClass = ( ) => {
68
- const { labelPosition, children, content, icon } = this . props
69
-
70
- if ( icon === true ) return true
71
- return icon && ( labelPosition || ( childrenUtils . isNil ( children ) && _ . isNil ( content ) ) )
139
+ _ . invoke ( props , 'onClick' , e , props )
72
140
}
73
141
74
- render ( ) {
75
- const {
76
- active,
77
- animated,
78
- attached,
79
- basic,
80
- children,
81
- circular,
82
- className,
83
- color,
84
- compact,
85
- content,
86
- disabled,
87
- floated,
88
- fluid,
89
- icon,
90
- inverted,
91
- label,
92
- labelPosition,
93
- loading,
94
- negative,
95
- positive,
96
- primary,
97
- secondary,
98
- size,
99
- toggle,
100
- type,
101
- } = this . props
102
-
103
- const baseClasses = cx (
104
- color ,
105
- size ,
106
- useKeyOnly ( active , 'active' ) ,
107
- useKeyOnly ( basic , 'basic' ) ,
108
- useKeyOnly ( circular , 'circular' ) ,
109
- useKeyOnly ( compact , 'compact' ) ,
110
- useKeyOnly ( fluid , 'fluid' ) ,
111
- useKeyOnly ( this . hasIconClass ( ) , 'icon' ) ,
112
- useKeyOnly ( inverted , 'inverted' ) ,
113
- useKeyOnly ( loading , 'loading' ) ,
114
- useKeyOnly ( negative , 'negative' ) ,
115
- useKeyOnly ( positive , 'positive' ) ,
116
- useKeyOnly ( primary , 'primary' ) ,
117
- useKeyOnly ( secondary , 'secondary' ) ,
118
- useKeyOnly ( toggle , 'toggle' ) ,
119
- useKeyOrValueAndKey ( animated , 'animated' ) ,
120
- useKeyOrValueAndKey ( attached , 'attached' ) ,
121
- )
122
- const labeledClasses = cx ( useKeyOrValueAndKey ( labelPosition || ! ! label , 'labeled' ) )
123
- const wrapperClasses = cx ( useKeyOnly ( disabled , 'disabled' ) , useValueAndKey ( floated , 'floated' ) )
124
-
125
- const rest = getUnhandledProps ( Button , this . props )
126
- const ElementType = getElementType ( Button , this . props , this . computeElementType )
127
- const tabIndex = this . computeTabIndex ( ElementType )
128
-
129
- if ( ! _ . isNil ( label ) ) {
130
- const buttonClasses = cx ( 'ui' , baseClasses , 'button' , className )
131
- const containerClasses = cx ( 'ui' , labeledClasses , 'button' , className , wrapperClasses )
132
- const labelElement = Label . create ( label , {
133
- defaultProps : {
134
- basic : true ,
135
- pointing : labelPosition === 'left' ? 'right' : 'left' ,
136
- } ,
137
- autoGenerateKey : false ,
138
- } )
139
-
140
- return (
141
- < ElementType { ...rest } className = { containerClasses } onClick = { this . handleClick } >
142
- { labelPosition === 'left' && labelElement }
143
- < Ref innerRef = { this . ref } >
144
- < button
145
- className = { buttonClasses }
146
- aria-pressed = { toggle ? ! ! active : undefined }
147
- disabled = { disabled }
148
- type = { type }
149
- tabIndex = { tabIndex }
150
- >
151
- { Icon . create ( icon , { autoGenerateKey : false } ) } { content }
152
- </ button >
153
- </ Ref >
154
- { ( labelPosition === 'right' || ! labelPosition ) && labelElement }
155
- </ ElementType >
156
- )
157
- }
158
-
159
- const classes = cx ( 'ui' , baseClasses , wrapperClasses , labeledClasses , 'button' , className )
160
- const hasChildren = ! childrenUtils . isNil ( children )
161
- const role = this . computeButtonAriaRole ( ElementType )
142
+ if ( ! _ . isNil ( label ) ) {
143
+ const buttonClasses = cx ( 'ui' , baseClasses , 'button' , className )
144
+ const containerClasses = cx ( 'ui' , labeledClasses , 'button' , className , wrapperClasses )
145
+ const labelElement = Label . create ( label , {
146
+ defaultProps : {
147
+ basic : true ,
148
+ pointing : labelPosition === 'left' ? 'right' : 'left' ,
149
+ } ,
150
+ autoGenerateKey : false ,
151
+ } )
162
152
163
153
return (
164
- < Ref innerRef = { this . ref } >
165
- < ElementType
166
- { ... rest }
167
- className = { classes }
154
+ < ElementType { ... rest } className = { containerClasses } onClick = { handleClick } >
155
+ { labelPosition === 'left' && labelElement }
156
+ < button
157
+ className = { buttonClasses }
168
158
aria-pressed = { toggle ? ! ! active : undefined }
169
- disabled = { ( disabled && ElementType === 'button' ) || undefined }
170
- onClick = { this . handleClick }
171
- role = { role }
172
- type = { type }
159
+ disabled = { disabled }
173
160
tabIndex = { tabIndex }
161
+ type = { type }
162
+ ref = { elementRef }
174
163
>
175
- { hasChildren && children }
176
- { ! hasChildren && Icon . create ( icon , { autoGenerateKey : false } ) }
177
- { ! hasChildren && content }
178
- </ ElementType >
179
- </ Ref >
164
+ { Icon . create ( icon , { autoGenerateKey : false } ) } { content }
165
+ </ button >
166
+ { ( labelPosition === 'right' || ! labelPosition ) && labelElement }
167
+ </ ElementType >
180
168
)
181
169
}
182
- }
183
170
171
+ const classes = cx ( 'ui' , baseClasses , wrapperClasses , labeledClasses , 'button' , className )
172
+ const hasChildren = ! childrenUtils . isNil ( children )
173
+ const role = computeButtonAriaRole ( ElementType , props . role )
174
+
175
+ return (
176
+ < ElementType
177
+ { ...rest }
178
+ className = { classes }
179
+ aria-pressed = { toggle ? ! ! active : undefined }
180
+ disabled = { ( disabled && ElementType === 'button' ) || undefined }
181
+ onClick = { handleClick }
182
+ role = { role }
183
+ tabIndex = { tabIndex }
184
+ type = { type }
185
+ ref = { elementRef }
186
+ >
187
+ { hasChildren && children }
188
+ { ! hasChildren && Icon . create ( icon , { autoGenerateKey : false } ) }
189
+ { ! hasChildren && content }
190
+ </ ElementType >
191
+ )
192
+ } )
193
+
194
+ Button . displayName = 'Button'
184
195
Button . propTypes = {
185
196
/** An element type to render as (string or function). */
186
197
as : PropTypes . elementType ,
0 commit comments