Skip to content

Commit 815d3bd

Browse files
committed
refactor: use react-hooks
1 parent 457d57a commit 815d3bd

File tree

7 files changed

+571
-669
lines changed

7 files changed

+571
-669
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
"typescript": "^5.8.3"
5353
},
5454
"peerDependencies": {
55-
"react": ">=17",
56-
"react-dom": ">=17"
55+
"react": ">=18",
56+
"react-dom": ">=18"
5757
},
5858
"dependencies": {
5959
"classnames": "^2.5.1",
@@ -62,9 +62,9 @@
6262
"raf": "^3.4.1"
6363
},
6464
"keywords": [
65-
"react",
66-
"react-time-picker",
6765
"time-picker",
66+
"react-time-picker",
67+
"react",
6868
"time",
6969
"picker"
7070
]

src/Combobox.tsx

Lines changed: 139 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Component } from 'react';
1+
import React, { useState, useCallback } from 'react';
22
import { set } from 'date-fns/set';
33
import { getHours } from 'date-fns/getHours';
44
import { 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(/\sA/) ? 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

279266
export default Combobox;

0 commit comments

Comments
 (0)