1- import React , { Component } from 'react' ;
1+ import React , { useState , useCallback } from 'react' ;
22import { set } from 'date-fns/set' ;
33import { getHours } from 'date-fns/getHours' ;
44import { getMinutes } from 'date-fns/getMinutes' ;
@@ -29,74 +29,115 @@ type Props = {
2929 disabledSeconds : ( hour : number | null , minute : number | null ) => number [ ] ;
3030} ;
3131
32- class Combobox extends Component < Props , { selectFocusOn : null | Selector } > {
33- constructor ( props : Props ) {
34- super ( props ) ;
35-
36- this . state = {
37- selectFocusOn : null ,
38- } ;
39-
40- this . onItemChange = this . onItemChange . bind ( this ) ;
41- this . handleKeyDown = this . handleKeyDown . bind ( this ) ;
42- this . getColumns = this . getColumns . bind ( this ) ;
43- }
44-
45- onItemChange ( type : Selector , itemValue : string ) {
46- const { onChange, defaultOpenValue, use12Hours, isAM, onAmPmChange } =
47- this . props ;
48- const value = this . props . value || defaultOpenValue ;
49-
50- let hour = getHours ( value ) ;
51- let minute = getMinutes ( value ) ;
52- let second = getSeconds ( value ) ;
53-
54- if ( type === 'hour' ) {
55- if ( use12Hours ) {
56- if ( isAM ) {
57- hour = + itemValue % 12 ;
32+ function Combobox ( props : Props ) {
33+ const [ selectFocusOn , setSelectFocusOn ] = useState < null | Selector > ( null ) ;
34+
35+ const onItemChange = useCallback (
36+ ( type : Selector , itemValue : string ) => {
37+ const { onChange, defaultOpenValue, use12Hours, isAM, onAmPmChange } =
38+ props ;
39+ const value = props . value || defaultOpenValue ;
40+
41+ let hour = getHours ( value ) ;
42+ let minute = getMinutes ( value ) ;
43+ let second = getSeconds ( value ) ;
44+
45+ if ( type === 'hour' ) {
46+ if ( use12Hours ) {
47+ if ( isAM ) {
48+ hour = + itemValue % 12 ;
49+ } else {
50+ hour = ( + itemValue % 12 ) + 12 ;
51+ }
5852 } else {
59- hour = ( + itemValue % 12 ) + 12 ;
53+ hour = + itemValue ;
6054 }
55+ } else if ( type === 'minute' ) {
56+ minute = + itemValue ;
57+ } else if ( type === 'ampm' ) {
58+ const ampm = itemValue . toUpperCase ( ) ;
59+ if ( use12Hours ) {
60+ if ( ampm === 'PM' && hour < 12 ) {
61+ hour = ( hour % 12 ) + 12 ;
62+ }
63+ if ( ampm === 'AM' ) {
64+ if ( hour >= 12 ) {
65+ hour = hour - 12 ;
66+ }
67+ }
68+ }
69+ onAmPmChange ( ampm ) ;
6170 } else {
62- hour = + itemValue ;
71+ second = + itemValue ;
6372 }
64- } else if ( type === 'minute' ) {
65- minute = + itemValue ;
66- } else if ( type === 'ampm' ) {
67- const ampm = itemValue . toUpperCase ( ) ;
68- if ( use12Hours ) {
69- if ( ampm === 'PM' && hour < 12 ) {
70- hour = ( hour % 12 ) + 12 ;
71- }
7273
73- if ( ampm === 'AM' ) {
74- if ( hour >= 12 ) {
75- hour = hour - 12 ;
76- }
77- }
74+ onChange (
75+ set ( value , {
76+ hours : hour ,
77+ minutes : minute ,
78+ seconds : second ,
79+ } ) ,
80+ ) ;
81+ } ,
82+ [
83+ props . onChange ,
84+ props . defaultOpenValue ,
85+ props . use12Hours ,
86+ props . isAM ,
87+ props . onAmPmChange ,
88+ props . value ,
89+ ] ,
90+ ) ;
91+
92+ const getColumns = useCallback ( ( ) => {
93+ const { showHour, showMinute, showSecond, use12Hours } = props ;
94+ return [
95+ [ 'hour' , showHour ] ,
96+ [ 'minute' , showMinute ] ,
97+ [ 'second' , showSecond ] ,
98+ [ 'ampm' , use12Hours ] ,
99+ ]
100+ . filter ( ( [ , enabled ] ) => enabled )
101+ . map ( ( [ val ] ) => val as Selector ) ;
102+ } , [ props . showHour , props . showMinute , props . showSecond , props . use12Hours ] ) ;
103+
104+ const changeFocusTo = useCallback (
105+ ( currentSelectType : Selector , offset : number ) => {
106+ const columns = getColumns ( ) ;
107+ const currentIndex = columns . indexOf ( currentSelectType ) ;
108+ let newIndex = currentIndex + offset ;
109+
110+ if ( newIndex < 0 ) {
111+ newIndex = columns . length - 1 ;
112+ } else if ( newIndex >= columns . length ) {
113+ newIndex = 0 ;
78114 }
79- onAmPmChange ( ampm ) ;
80- } else {
81- second = + itemValue ;
82- }
83115
84- onChange (
85- set ( value , {
86- hours : hour ,
87- minutes : minute ,
88- seconds : second ,
89- } ) ,
90- ) ;
91- }
116+ const newFocusOn = columns [ newIndex ] ;
117+ setSelectFocusOn ( newFocusOn ) ;
118+ } ,
119+ [ getColumns ] ,
120+ ) ;
121+
122+ const handleKeyDown = useCallback (
123+ ( currentType : Selector , e : React . KeyboardEvent < HTMLElement > ) => {
124+ if ( e . keyCode === 39 ) {
125+ changeFocusTo ( currentType , 1 ) ;
126+ e . preventDefault ( ) ;
127+ e . stopPropagation ( ) ;
128+ } else if ( e . keyCode === 37 ) {
129+ changeFocusTo ( currentType , - 1 ) ;
130+ e . preventDefault ( ) ;
131+ e . stopPropagation ( ) ;
132+ }
133+ } ,
134+ [ changeFocusTo ] ,
135+ ) ;
92136
93- getHourSelect ( hour : number ) {
137+ const getHourSelect = ( hour : number ) => {
94138 const { prefixCls, hourOptions, disabledHours, showHour, use12Hours } =
95- this . props ;
96-
97- if ( ! showHour ) {
98- return null ;
99- }
139+ props ;
140+ if ( ! showHour ) return null ;
100141
101142 const disabledOptions = disabledHours ( ) ;
102143
@@ -120,25 +161,23 @@ class Combobox extends Component<Props, { selectFocusOn: null | Selector }> {
120161 selectedIndex = { hourOptionsAdj . indexOf ( hourAdj ) }
121162 type = "hour"
122163 label = "hour"
123- onSelect = { this . onItemChange }
124- onKeyDown = { ( e ) => this . handleKeyDown ( 'hour' , e ) }
125- focused = { this . state . selectFocusOn === 'hour' }
164+ onSelect = { onItemChange }
165+ onKeyDown = { ( e ) => handleKeyDown ( 'hour' , e ) }
166+ focused = { selectFocusOn === 'hour' }
126167 />
127168 ) ;
128- }
169+ } ;
129170
130- getMinuteSelect ( minute : number ) {
171+ const getMinuteSelect = ( minute : number ) => {
131172 const {
132173 prefixCls,
133174 minuteOptions,
134175 disabledMinutes,
135176 defaultOpenValue,
136177 showMinute,
137178 value : propValue ,
138- } = this . props ;
139- if ( ! showMinute ) {
140- return null ;
141- }
179+ } = props ;
180+ if ( ! showMinute ) return null ;
142181 const value = propValue || defaultOpenValue ;
143182 const disabledOptions = disabledMinutes ( getHours ( value ) ) ;
144183
@@ -151,25 +190,23 @@ class Combobox extends Component<Props, { selectFocusOn: null | Selector }> {
151190 selectedIndex = { minuteOptions . indexOf ( minute ) }
152191 type = "minute"
153192 label = "minute"
154- onSelect = { this . onItemChange }
155- onKeyDown = { ( e ) => this . handleKeyDown ( 'minute' , e ) }
156- focused = { this . state . selectFocusOn === 'minute' }
193+ onSelect = { onItemChange }
194+ onKeyDown = { ( e ) => handleKeyDown ( 'minute' , e ) }
195+ focused = { selectFocusOn === 'minute' }
157196 />
158197 ) ;
159- }
198+ } ;
160199
161- getSecondSelect ( second : number ) {
200+ const getSecondSelect = ( second : number ) => {
162201 const {
163202 prefixCls,
164203 secondOptions,
165204 disabledSeconds,
166205 showSecond,
167206 defaultOpenValue,
168207 value : propValue ,
169- } = this . props ;
170- if ( ! showSecond ) {
171- return null ;
172- }
208+ } = props ;
209+ if ( ! showSecond ) return null ;
173210 const value = propValue || defaultOpenValue ;
174211 const disabledOptions = disabledSeconds ( getHours ( value ) , getMinutes ( value ) ) ;
175212
@@ -182,21 +219,18 @@ class Combobox extends Component<Props, { selectFocusOn: null | Selector }> {
182219 selectedIndex = { secondOptions . indexOf ( second ) }
183220 type = "second"
184221 label = "second"
185- onSelect = { this . onItemChange }
186- onKeyDown = { ( e ) => this . handleKeyDown ( 'second' , e ) }
187- focused = { this . state . selectFocusOn === 'second' }
222+ onSelect = { onItemChange }
223+ onKeyDown = { ( e ) => handleKeyDown ( 'second' , e ) }
224+ focused = { selectFocusOn === 'second' }
188225 />
189226 ) ;
190- }
227+ } ;
191228
192- getAMPMSelect ( ) {
193- const { prefixCls, use12Hours, format, isAM } = this . props ;
194-
195- if ( ! use12Hours ) {
196- return null ;
197- }
229+ const getAMPMSelect = ( ) => {
230+ const { prefixCls, use12Hours, format, isAM } = props ;
231+ if ( ! use12Hours ) return null ;
198232
199- const AMPMOptions = [ 'am' , 'pm' ] // If format has A char, then we should uppercase AM/PM
233+ const AMPMOptions = [ 'am' , 'pm' ]
200234 . map ( ( c ) => ( format . match ( / \s A / ) ? c . toUpperCase ( ) : c ) )
201235 . map ( ( c ) => ( { value : c , disabled : false } ) ) ;
202236
@@ -209,71 +243,24 @@ class Combobox extends Component<Props, { selectFocusOn: null | Selector }> {
209243 selectedIndex = { selected }
210244 type = "ampm"
211245 label = "AM or PM"
212- onSelect = { this . onItemChange }
213- onKeyDown = { ( e ) => this . handleKeyDown ( 'ampm' , e ) }
214- focused = { this . state . selectFocusOn === 'ampm' }
246+ onSelect = { onItemChange }
247+ onKeyDown = { ( e ) => handleKeyDown ( 'ampm' , e ) }
248+ focused = { selectFocusOn === 'ampm' }
215249 />
216250 ) ;
217- }
218-
219- handleKeyDown ( currentType : Selector , e : React . KeyboardEvent < HTMLElement > ) {
220- if ( e . keyCode === 39 ) {
221- // right arrow
222- this . changeFocusTo ( currentType , 1 ) ;
223- e . preventDefault ( ) ;
224- e . stopPropagation ( ) ;
225- } else if ( e . keyCode === 37 ) {
226- // left arrow
227- this . changeFocusTo ( currentType , - 1 ) ;
228- e . preventDefault ( ) ;
229- e . stopPropagation ( ) ;
230- }
231- }
232-
233- getColumns ( ) {
234- // get list of enabled columns (e.g. ['hour', 'minute', 'ampm'])
235- const { showHour, showMinute, showSecond, use12Hours } = this . props ;
236-
237- return [
238- [ 'hour' , showHour ] ,
239- [ 'minute' , showMinute ] ,
240- [ 'second' , showSecond ] ,
241- [ 'ampm' , use12Hours ] ,
242- ]
243- . filter ( ( [ , enabled ] ) => enabled )
244- . map ( ( [ val ] ) => val as Selector ) ;
245- }
246-
247- changeFocusTo ( currentSelectType : Selector , offset : number ) {
248- const columns = this . getColumns ( ) ;
249-
250- const currentIndex = columns . indexOf ( currentSelectType ) ;
251- let newIndex = currentIndex + offset ;
252-
253- // bounds + wrap
254- if ( newIndex < 0 ) {
255- newIndex = columns . length - 1 ;
256- } else if ( newIndex >= columns . length ) {
257- newIndex = 0 ;
258- }
259-
260- const newFocusOn = columns [ newIndex ] ;
261-
262- this . setState ( { selectFocusOn : newFocusOn } ) ;
263- }
264-
265- render ( ) {
266- const { prefixCls, defaultOpenValue, value : propValue } = this . props ;
267- const value = propValue || defaultOpenValue ;
268- return (
269- < div className = { `${ prefixCls } -combobox` } >
270- { this . getHourSelect ( getHours ( value ) ) }
271- { this . getMinuteSelect ( getMinutes ( value ) ) }
272- { this . getSecondSelect ( getSeconds ( value ) ) }
273- { this . getAMPMSelect ( ) }
274- </ div >
275- ) ;
276- }
251+ } ;
252+
253+ const { prefixCls, defaultOpenValue, value : propValue } = props ;
254+ const value = propValue || defaultOpenValue ;
255+
256+ return (
257+ < div className = { `${ prefixCls } -combobox` } >
258+ { getHourSelect ( getHours ( value ) ) }
259+ { getMinuteSelect ( getMinutes ( value ) ) }
260+ { getSecondSelect ( getSeconds ( value ) ) }
261+ { getAMPMSelect ( ) }
262+ </ div >
263+ ) ;
277264}
278265
279266export default Combobox ;
0 commit comments