Skip to content

Commit b34f58e

Browse files
committed
feat: Support milliseconds picker column (#149)
1 parent 88fa1ba commit b34f58e

File tree

9 files changed

+182
-20
lines changed

9 files changed

+182
-20
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,12 @@ API
6868
| showHour | Boolean | true | whether show hour | |
6969
| showMinute | Boolean | true | whether show minute |
7070
| showSecond | Boolean | true | whether show second |
71+
| showMillisecond | Boolean | false | whether show millisecond |
7172
| format | String | - | moment format |
7273
| disabledHours | Function | - | disabled hour options |
7374
| disabledMinutes | Function | - | disabled minute options |
7475
| disabledSeconds | Function | - | disabled second options |
76+
| disabledMilliseconds | Function | - | disabled millisecond options |
7577
| use12Hours | Boolean | false | 12 hours display mode |
7678
| hideDisabledOptions | Boolean | false | whether hide disabled options |
7779
| onChange | Function | null | called when time-picker a different value |
@@ -85,10 +87,11 @@ API
8587
| hourStep | Number | 1 | interval between hours in picker |
8688
| minuteStep | Number | 1 | interval between minutes in picker |
8789
| secondStep | Number | 1 | interval between seconds in picker |
90+
| millisecondStep | Number | 1 | interval between milliseconds in picker |
8891
| focusOnOpen | Boolean | false | automatically focus the input when the picker opens |
89-
| inputReadOnly | Boolean | false | set input to read only |
90-
| inputIcon | ReactNode | | specific the time-picker icon. |
91-
| clearIcon | ReactNode | | specific the clear icon. |
92+
| inputReadOnly | Boolean | false | set input to read only |
93+
| inputIcon | ReactNode | | specific the select icon. |
94+
| clearIcon | ReactNode | | specific the clear icon. |
9295

9396
## Test Case
9497

assets/index/Panel.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@
2626
&-narrow {
2727
max-width: 113px;
2828
}
29+
30+
&-wide {
31+
width: 227px;
32+
}
2933
}

examples/disabled.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,25 @@ function disabledSeconds(h, m) {
4545
return [h + (m % 60)];
4646
}
4747

48+
function disabledMilliseconds() {
49+
return [1, 5, 10];
50+
}
51+
4852
const App = () => (
4953
<>
5054
<h3>Disabled picker</h3>
5155
<TimePicker defaultValue={now} disabled onChange={onChange} />
5256
<h3>Disabled options</h3>
5357
<TimePicker
5458
showSecond={showSecond}
59+
showMillisecond
5560
defaultValue={now}
5661
className="xxx"
5762
onChange={onChange}
5863
disabledHours={disabledHours}
5964
disabledMinutes={disabledMinutes}
6065
disabledSeconds={disabledSeconds}
66+
disabledMilliseconds={disabledMilliseconds}
6167
/>
6268
</>
6369
);

examples/format.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const App = () => (
88
<TimePicker defaultValue={moment()} showHour={false} />
99
<TimePicker defaultValue={moment()} showMinute={false} />
1010
<TimePicker defaultValue={moment()} showSecond={false} />
11+
<TimePicker defaultValue={moment()} showMillisecond />
1112

1213
<TimePicker defaultValue={moment()} showMinute={false} showSecond={false} />
1314
<TimePicker defaultValue={moment()} showHour={false} showSecond={false} />

rc-time-picker.d.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
declare module 'rc-time-picker' {
2+
import { Moment } from 'moment';
3+
import * as React from 'react';
4+
5+
type TimePickerProps = {
6+
prefixCls?: String;
7+
clearText?: String;
8+
disabled?: Boolean;
9+
allowEmpty?: Boolean;
10+
open?: Boolean;
11+
defaultValue?: Moment;
12+
defaultOpenValue?: Moment;
13+
value?: Moment;
14+
placeholder?: String;
15+
className?: String;
16+
id?: String;
17+
popupClassName?: String;
18+
showHour?: Boolean;
19+
showMinute?: Boolean;
20+
showSecond?: Boolean;
21+
showMillisecond?: Boolean;
22+
format?: String;
23+
disabledHours?: Function;
24+
disabledMinutes?: Function;
25+
disabledSeconds?: Function;
26+
disabledMilliseconds?: Function;
27+
use12Hours?: Boolean;
28+
hideDisabledOptions?: Boolean;
29+
onChange?: Function;
30+
addon?: Function;
31+
placement?: String;
32+
transitionName?: String;
33+
name?: String;
34+
onOpen?: Function;
35+
onClose?: Function;
36+
hourStep?: Number;
37+
minuteStep?: Number;
38+
secondStep?: Number;
39+
millisecondStep?: Number;
40+
focusOnOpen?: Boolean;
41+
inputReadOnly?: Boolean;
42+
inputIcon?: React.ReactNode;
43+
clearIcon?: React.ReactNode;
44+
};
45+
export default class TimePicker extends React.Component<TimePickerProps> {}
46+
}

src/Combobox.jsx

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ class Combobox extends Component {
5656
}
5757
}
5858
onAmPmChange(ampm);
59-
} else {
59+
} else if (type === 'second') {
6060
value.second(+itemValue);
61+
} else {
62+
itemValue += '0';
63+
value.millisecond(+itemValue);
6164
}
6265
onChange(value);
6366
};
@@ -154,6 +157,33 @@ class Combobox extends Component {
154157
);
155158
}
156159

160+
getMillisecondSelect(millisecond) {
161+
const {
162+
prefixCls,
163+
millisecondOptions,
164+
disabledMilliseconds,
165+
showMillisecond,
166+
defaultOpenValue,
167+
value: propValue,
168+
} = this.props;
169+
if (!showMillisecond) {
170+
return null;
171+
}
172+
const value = propValue || defaultOpenValue;
173+
const disabledOptions = disabledMilliseconds(value.hour(), value.minute(), value.second());
174+
175+
return (
176+
<Select
177+
prefixCls={prefixCls}
178+
options={millisecondOptions.map(option => formatOption(option, disabledOptions))}
179+
selectedIndex={millisecondOptions.indexOf(Math.floor(millisecond / 10))}
180+
type="millisecond"
181+
onSelect={this.onItemChange}
182+
onMouseEnter={() => this.onEnterSelectPanel('millisecond')}
183+
/>
184+
);
185+
}
186+
157187
getAMPMSelect() {
158188
const { prefixCls, use12Hours, format, isAM, onEsc } = this.props;
159189
if (!use12Hours) {
@@ -187,6 +217,7 @@ class Combobox extends Component {
187217
{this.getHourSelect(value.hour())}
188218
{this.getMinuteSelect(value.minute())}
189219
{this.getSecondSelect(value.second())}
220+
{this.getMillisecondSelect(value.millisecond())}
190221
{this.getAMPMSelect(value.hour())}
191222
</div>
192223
);

src/Header.jsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ class Header extends Component {
5757
hourOptions,
5858
minuteOptions,
5959
secondOptions,
60+
millisecondOptions,
6061
disabledHours,
6162
disabledMinutes,
6263
disabledSeconds,
64+
disabledMilliseconds,
6365
onChange,
6466
} = this.props;
6567

@@ -76,13 +78,15 @@ class Header extends Component {
7678
value
7779
.hour(parsed.hour())
7880
.minute(parsed.minute())
79-
.second(parsed.second());
81+
.second(parsed.second())
82+
.millisecond(parsed.millisecond());
8083

8184
// if time value not allowed, response warning.
8285
if (
8386
hourOptions.indexOf(value.hour()) < 0 ||
8487
minuteOptions.indexOf(value.minute()) < 0 ||
85-
secondOptions.indexOf(value.second()) < 0
88+
secondOptions.indexOf(value.second()) < 0 ||
89+
millisecondOptions.indexOf(value.millisecond()) < 0
8690
) {
8791
this.setState({
8892
invalid: true,
@@ -94,10 +98,16 @@ class Header extends Component {
9498
const disabledHourOptions = disabledHours();
9599
const disabledMinuteOptions = disabledMinutes(value.hour());
96100
const disabledSecondOptions = disabledSeconds(value.hour(), value.minute());
101+
const disabledMillisecondOptions = disabledMilliseconds(
102+
value.hour(),
103+
value.minute(),
104+
value.second(),
105+
);
97106
if (
98107
(disabledHourOptions && disabledHourOptions.indexOf(value.hour()) >= 0) ||
99108
(disabledMinuteOptions && disabledMinuteOptions.indexOf(value.minute()) >= 0) ||
100-
(disabledSecondOptions && disabledSecondOptions.indexOf(value.second()) >= 0)
109+
(disabledSecondOptions && disabledSecondOptions.indexOf(value.second()) >= 0) ||
110+
(disabledMillisecondOptions && disabledMillisecondOptions.indexOf(value.millisecond()) >= 0)
101111
) {
102112
this.setState({
103113
invalid: true,
@@ -109,13 +119,15 @@ class Header extends Component {
109119
if (
110120
originalValue.hour() !== value.hour() ||
111121
originalValue.minute() !== value.minute() ||
112-
originalValue.second() !== value.second()
122+
originalValue.second() !== value.second() ||
123+
originalValue.millisecond() !== value.millisecond()
113124
) {
114125
// keep other fields for rc-calendar
115126
const changedValue = originalValue.clone();
116127
changedValue.hour(value.hour());
117128
changedValue.minute(value.minute());
118129
changedValue.second(value.second());
130+
changedValue.millisecond(value.millisecond());
119131
onChange(changedValue);
120132
}
121133
} else if (originalValue !== value) {

src/Panel.jsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import classNames from 'classnames';
44
import Header from './Header';
55
import Combobox from './Combobox';
66

7-
function noop() {}
7+
function noop() { }
88

99
function generateOptions(length, disabledOptions, hideDisabledOptions, step = 1) {
1010
const arr = [];
@@ -16,7 +16,7 @@ function generateOptions(length, disabledOptions, hideDisabledOptions, step = 1)
1616
return arr;
1717
}
1818

19-
function toNearestValidTime(time, hourOptions, minuteOptions, secondOptions) {
19+
function toNearestValidTime(time, hourOptions, minuteOptions, secondOptions, millisecondOptions) {
2020
const hour = hourOptions
2121
.slice()
2222
.sort((a, b) => Math.abs(time.hour() - a) - Math.abs(time.hour() - b))[0];
@@ -26,7 +26,10 @@ function toNearestValidTime(time, hourOptions, minuteOptions, secondOptions) {
2626
const second = secondOptions
2727
.slice()
2828
.sort((a, b) => Math.abs(time.second() - a) - Math.abs(time.second() - b))[0];
29-
return moment(`${hour}:${minute}:${second}`, 'HH:mm:ss');
29+
const millisecond = millisecondOptions
30+
.slice()
31+
.sort((a, b) => Math.abs(time.millisecond() - a) - Math.abs(time.millisecond() - b))[0];
32+
return moment(`${hour}:${minute}:${second}:${millisecond}`, 'HH:mm:ss:SS');
3033
}
3134

3235
class Panel extends Component {
@@ -36,6 +39,7 @@ class Panel extends Component {
3639
disabledHours: noop,
3740
disabledMinutes: noop,
3841
disabledSeconds: noop,
42+
disabledMilliseconds: noop,
3943
defaultOpenValue: moment(),
4044
use12Hours: false,
4145
addon: noop,
@@ -104,10 +108,12 @@ class Panel extends Component {
104108
placeholder,
105109
disabledMinutes,
106110
disabledSeconds,
111+
disabledMilliseconds,
107112
hideDisabledOptions,
108113
showHour,
109114
showMinute,
110115
showSecond,
116+
showMillisecond,
111117
format,
112118
defaultOpenValue,
113119
clearText,
@@ -119,6 +125,7 @@ class Panel extends Component {
119125
hourStep,
120126
minuteStep,
121127
secondStep,
128+
millisecondStep,
122129
inputReadOnly,
123130
clearIcon,
124131
} = this.props;
@@ -129,6 +136,11 @@ class Panel extends Component {
129136
value ? value.hour() : null,
130137
value ? value.minute() : null,
131138
);
139+
const disabledMillisecondOptions = disabledMilliseconds(
140+
value ? value.hour() : null,
141+
value ? value.minute() : null,
142+
value ? value.second() : null,
143+
);
132144
const hourOptions = generateOptions(24, disabledHourOptions, hideDisabledOptions, hourStep);
133145
const minuteOptions = generateOptions(
134146
60,
@@ -142,12 +154,19 @@ class Panel extends Component {
142154
hideDisabledOptions,
143155
secondStep,
144156
);
157+
const millisecondOptions = generateOptions(
158+
100,
159+
disabledMillisecondOptions,
160+
hideDisabledOptions,
161+
millisecondStep,
162+
);
145163

146164
const validDefaultOpenValue = toNearestValidTime(
147165
defaultOpenValue,
148166
hourOptions,
149167
minuteOptions,
150168
secondOptions,
169+
millisecondOptions,
151170
);
152171

153172
return (
@@ -164,9 +183,11 @@ class Panel extends Component {
164183
hourOptions={hourOptions}
165184
minuteOptions={minuteOptions}
166185
secondOptions={secondOptions}
186+
millisecondOptions={millisecondOptions}
167187
disabledHours={this.disabledHours}
168188
disabledMinutes={disabledMinutes}
169189
disabledSeconds={disabledSeconds}
190+
disabledMilliseconds={disabledMilliseconds}
170191
onChange={this.onChange}
171192
focusOnOpen={focusOnOpen}
172193
onKeyDown={onKeyDown}
@@ -183,12 +204,15 @@ class Panel extends Component {
183204
showHour={showHour}
184205
showMinute={showMinute}
185206
showSecond={showSecond}
207+
showMillisecond={showMillisecond}
186208
hourOptions={hourOptions}
187209
minuteOptions={minuteOptions}
188210
secondOptions={secondOptions}
211+
millisecondOptions={millisecondOptions}
189212
disabledHours={this.disabledHours}
190213
disabledMinutes={disabledMinutes}
191214
disabledSeconds={disabledSeconds}
215+
disabledMilliseconds={disabledMilliseconds}
192216
onCurrentSelectPanelChange={this.onCurrentSelectPanelChange}
193217
use12Hours={use12Hours}
194218
onEsc={onEsc}

0 commit comments

Comments
 (0)