@@ -40,100 +40,94 @@ type TProps = {
40
40
setValue : ( value : string ) => void ;
41
41
} & ( TOptional | TRequired ) ;
42
42
43
- type TState = {
44
- popoverVisible : boolean ;
45
- } ;
46
-
47
43
export const DEFAULT_PLACEHOLDER = 'Select a value…' ;
48
44
49
- export default class NameSelector extends React . PureComponent < TProps , TState > {
50
- listRef : React . RefObject < FilteredList > = React . createRef ( ) ;
51
- state : TState = { popoverVisible : false } ;
45
+ function NameSelector ( props : TProps ) {
46
+ const listRef = React . useRef < FilteredList > ( null ) ;
47
+ const [ popoverVisible , setPopoverVisible ] = React . useState ( false ) ;
52
48
53
- componentDidUpdate ( ) {
54
- if ( this . listRef . current && this . state . popoverVisible ) {
55
- this . listRef . current . focusInput ( ) ;
49
+ React . useEffect ( ( ) => {
50
+ if ( listRef . current && popoverVisible ) {
51
+ listRef . current . focusInput ( ) ;
56
52
}
57
- }
53
+ } , [ popoverVisible ] ) ;
58
54
59
- private changeVisible ( popoverVisible : boolean ) {
60
- this . setState ( { popoverVisible } ) ;
55
+ const changeVisible = React . useCallback ( ( popoverVisible : boolean ) => {
56
+ setPopoverVisible ( popoverVisible ) ;
61
57
62
- // Defer registering a click handler to hide the selector popover
63
- // to avoid handling the click event that triggered opening the popover itself.
64
58
setTimeout ( ( ) => {
65
59
if ( popoverVisible ) {
66
- window . document . body . addEventListener ( 'click' , this . onBodyClicked ) ;
60
+ window . document . body . addEventListener ( 'click' , onBodyClicked ) ;
67
61
} else {
68
- window . document . body . removeEventListener ( 'click' , this . onBodyClicked ) ;
62
+ window . document . body . removeEventListener ( 'click' , onBodyClicked ) ;
69
63
}
70
64
} ) ;
71
- }
65
+ } , [ ] ) ;
72
66
73
- private clearValue = ( evt : React . MouseEvent ) => {
74
- if ( this . props . required ) throw new Error ( 'Cannot clear value of required NameSelector' ) ;
67
+ const clearValue = ( evt : React . MouseEvent ) => {
68
+ if ( props . required ) throw new Error ( 'Cannot clear value of required NameSelector' ) ;
75
69
76
70
evt . stopPropagation ( ) ;
77
- this . props . clearValue ( ) ;
71
+ props . clearValue ( ) ;
78
72
} ;
79
73
80
- setValue = ( value : string ) => {
81
- this . props . setValue ( value ) ;
82
- this . changeVisible ( false ) ;
74
+ const setValue = ( value : string ) => {
75
+ props . setValue ( value ) ;
76
+ changeVisible ( false ) ;
83
77
} ;
84
78
85
- private onBodyClicked = ( ) => {
86
- if ( this . listRef . current && ! this . listRef . current . isMouseWithin ( ) ) {
87
- this . changeVisible ( false ) ;
79
+ const onBodyClicked = React . useCallback ( ( ) => {
80
+ if ( listRef . current && ! listRef . current . isMouseWithin ( ) ) {
81
+ changeVisible ( false ) ;
88
82
}
89
- } ;
83
+ } , [ changeVisible ] ) ;
90
84
91
- onFilterCancelled = ( ) => {
92
- this . changeVisible ( false ) ;
85
+ const onFilterCancelled = ( ) => {
86
+ changeVisible ( false ) ;
93
87
} ;
94
88
95
- onPopoverVisibleChanged = ( popoverVisible : boolean ) => {
96
- this . changeVisible ( popoverVisible ) ;
89
+ const onPopoverVisibleChanged = ( visible : boolean ) => {
90
+ changeVisible ( visible ) ;
97
91
} ;
98
92
99
- render ( ) {
100
- const { label, options, placeholder = false , required = false , value } = this . props ;
101
- const { popoverVisible } = this . state ;
102
-
103
- const rootCls = cx ( 'NameSelector' , {
104
- 'is-active' : popoverVisible ,
105
- 'is-invalid' : required && ! value ,
106
- } ) ;
107
- let useLabel = true ;
108
- let text = value || '' ;
109
- if ( ! value && placeholder ) {
110
- useLabel = false ;
111
- text = typeof placeholder === 'string' ? placeholder : DEFAULT_PLACEHOLDER ;
112
- }
113
- return (
114
- < Popover
115
- overlayClassName = "NameSelector--overlay u-rm-popover-content-padding"
116
- onOpenChange = { this . onPopoverVisibleChanged }
117
- placement = "bottomLeft"
118
- content = {
119
- < FilteredList
120
- ref = { this . listRef }
121
- cancel = { this . onFilterCancelled }
122
- options = { options }
123
- value = { value }
124
- setValue = { this . setValue }
125
- />
126
- }
127
- trigger = "click"
128
- open = { popoverVisible }
129
- >
130
- < h2 className = { rootCls } >
131
- { useLabel && < span className = "NameSelector--label" > { label } :</ span > }
132
- < BreakableText className = "NameSelector--value" text = { text } />
133
- < IoChevronDown className = "NameSelector--chevron" />
134
- { ! required && value && < IoClose className = "NameSelector--clearIcon" onClick = { this . clearValue } /> }
135
- </ h2 >
136
- </ Popover >
137
- ) ;
93
+ const { label, options, placeholder = false , required = false , value } = props ;
94
+
95
+ const rootCls = cx ( 'NameSelector' , {
96
+ 'is-active' : popoverVisible ,
97
+ 'is-invalid' : required && ! value ,
98
+ } ) ;
99
+ let useLabel = true ;
100
+ let text = value || '' ;
101
+ if ( ! value && placeholder ) {
102
+ useLabel = false ;
103
+ text = typeof placeholder === 'string' ? placeholder : DEFAULT_PLACEHOLDER ;
138
104
}
105
+
106
+ return (
107
+ < Popover
108
+ overlayClassName = "NameSelector--overlay u-rm-popover-content-padding"
109
+ onOpenChange = { onPopoverVisibleChanged }
110
+ placement = "bottomLeft"
111
+ content = {
112
+ < FilteredList
113
+ ref = { listRef }
114
+ cancel = { onFilterCancelled }
115
+ options = { options }
116
+ value = { value }
117
+ setValue = { setValue }
118
+ />
119
+ }
120
+ trigger = "click"
121
+ open = { popoverVisible }
122
+ >
123
+ < h2 className = { rootCls } >
124
+ { useLabel && < span className = "NameSelector--label" > { label } :</ span > }
125
+ < BreakableText className = "NameSelector--value" text = { text } />
126
+ < IoChevronDown className = "NameSelector--chevron" />
127
+ { ! required && value && < IoClose className = "NameSelector--clearIcon" onClick = { clearValue } /> }
128
+ </ h2 >
129
+ </ Popover >
130
+ ) ;
139
131
}
132
+
133
+ export default NameSelector ;
0 commit comments