aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorafc163 <afc163@gmail.com>2017-03-08 15:00:31 +0800
committerafc163 <afc163@gmail.com>2017-03-08 15:00:31 +0800
commit4e020febdf6201ab5f10a0e49be81c6ff32daaeb (patch)
treee5636fff22c61a93236e4a7dfe1e00e87d371061
parent19b96283c09e678b8b7ea5c60569996a0bab5935 (diff)
parent369ffb9a3883cb01ff5d80cc2312a1a7f2fbe6a4 (diff)
downloadtime-picker-4e020febdf6201ab5f10a0e49be81c6ff32daaeb.tar.gz
time-picker-4e020febdf6201ab5f10a0e49be81c6ff32daaeb.tar.zst
time-picker-4e020febdf6201ab5f10a0e49be81c6ff32daaeb.zip
Merge branch 'shaleynikov-master'
-rw-r--r--README.md3
-rw-r--r--examples/12hours.html0
-rw-r--r--examples/12hours.js30
-rw-r--r--src/Combobox.jsx76
-rw-r--r--src/Panel.jsx5
-rw-r--r--src/Select.jsx2
-rw-r--r--src/TimePicker.jsx18
-rw-r--r--tests/Select.spec.jsx176
8 files changed, 298 insertions, 12 deletions
diff --git a/README.md b/README.md
index 9ecbc94..d8a35b9 100644
--- a/README.md
+++ b/README.md
@@ -60,12 +60,13 @@ API
60| value | moment | null | current value | 60| value | moment | null | current value |
61| placeholder | String | '' | time input's placeholder | 61| placeholder | String | '' | time input's placeholder |
62| showHour | Boolean | whether show hour | | 62| showHour | Boolean | whether show hour | |
63| showMinute | Boolean | whether show minute | | 63| showMinute | Boolean | whether show minute | |
64| showSecond | Boolean | whether show second | | 64| showSecond | Boolean | whether show second | |
65| format | String | | | 65| format | String | | |
66| disabledHours | Function | disabled hour options | | 66| disabledHours | Function | disabled hour options | |
67| disabledMinutes | Function | disabled minute options | | 67| disabledMinutes | Function | disabled minute options | |
68| disabledSeconds | Function | disabled second options | | 68| disabledSeconds | Function | disabled second options | |
69| use12Hours | Boolean | 12 hours display mode | |
69| hideDisabledOptions | Boolean | whether hide disabled options | | 70| hideDisabledOptions | Boolean | whether hide disabled options | |
70| onChange | Function | null | called when select a different value | 71| onChange | Function | null | called when select a different value |
71| addon | Function | nothing | called from timepicker panel to render some addon to its bottom, like an OK button. Receives panel instance as parameter, to be able to close it like `panel.close()`.| 72| addon | Function | nothing | called from timepicker panel to render some addon to its bottom, like an OK button. Receives panel instance as parameter, to be able to close it like `panel.close()`.|
diff --git a/examples/12hours.html b/examples/12hours.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/examples/12hours.html
diff --git a/examples/12hours.js b/examples/12hours.js
new file mode 100644
index 0000000..72d6950
--- /dev/null
+++ b/examples/12hours.js
@@ -0,0 +1,30 @@
1/* eslint no-console:0 */
2
3import 'rc-time-picker/assets/index.less';
4
5import React from 'react';
6import ReactDom from 'react-dom';
7
8import moment from 'moment';
9
10import TimePicker from 'rc-time-picker';
11
12const format = 'h:mm a';
13
14const now = moment().hour(0).minute(0);
15
16function onChange(value) {
17 console.log(value && value.format(format));
18}
19
20ReactDom.render(
21 <TimePicker
22 showSecond={false}
23 defaultValue={now}
24 className="xxx"
25 onChange={onChange}
26 format={format}
27 use12Hours
28 />,
29 document.getElementById('__react-content')
30);
diff --git a/src/Combobox.jsx b/src/Combobox.jsx
index 013617c..36b61cc 100644
--- a/src/Combobox.jsx
+++ b/src/Combobox.jsx
@@ -35,17 +35,40 @@ const Combobox = React.createClass({
35 disabledMinutes: PropTypes.func, 35 disabledMinutes: PropTypes.func,
36 disabledSeconds: PropTypes.func, 36 disabledSeconds: PropTypes.func,
37 onCurrentSelectPanelChange: PropTypes.func, 37 onCurrentSelectPanelChange: PropTypes.func,
38 use12Hours: PropTypes.bool,
38 }, 39 },
39 40
40 onItemChange(type, itemValue) { 41 onItemChange(type, itemValue) {
41 const { onChange, defaultOpenValue } = this.props; 42 const { onChange, defaultOpenValue, use12Hours } = this.props;
42 const value = (this.props.value || defaultOpenValue).clone(); 43 const value = (this.props.value || defaultOpenValue).clone();
44
43 if (type === 'hour') { 45 if (type === 'hour') {
44 value.hour(itemValue); 46 if (use12Hours) {
47 if (this.isAM()) {
48 value.hour(+itemValue % 12);
49 } else {
50 value.hour((+itemValue % 12) + 12);
51 }
52 } else {
53 value.hour(+itemValue);
54 }
45 } else if (type === 'minute') { 55 } else if (type === 'minute') {
46 value.minute(itemValue); 56 value.minute(+itemValue);
57 } else if (type === 'ampm') {
58 const ampm = itemValue.toUpperCase();
59 if (use12Hours) {
60 if (ampm === 'PM' && value.hour() < 12) {
61 value.hour((value.hour() % 12) + 12);
62 }
63
64 if (ampm === 'AM') {
65 if (value.hour() >= 12) {
66 value.hour(value.hour() - 12);
67 }
68 }
69 }
47 } else { 70 } else {
48 value.second(itemValue); 71 value.second(+itemValue);
49 } 72 }
50 onChange(value); 73 onChange(value);
51 }, 74 },
@@ -55,17 +78,26 @@ const Combobox = React.createClass({
55 }, 78 },
56 79
57 getHourSelect(hour) { 80 getHourSelect(hour) {
58 const { prefixCls, hourOptions, disabledHours, showHour } = this.props; 81 const { prefixCls, hourOptions, disabledHours, showHour, use12Hours } = this.props;
59 if (!showHour) { 82 if (!showHour) {
60 return null; 83 return null;
61 } 84 }
62 const disabledOptions = disabledHours(); 85 const disabledOptions = disabledHours();
86 let hourOptionsAdj;
87 let hourAdj;
88 if (use12Hours) {
89 hourOptionsAdj = [12].concat(hourOptions.filter(h => h < 12 && h > 0));
90 hourAdj = (hour % 12) || 12;
91 } else {
92 hourOptionsAdj = hourOptions;
93 hourAdj = hour;
94 }
63 95
64 return ( 96 return (
65 <Select 97 <Select
66 prefixCls={prefixCls} 98 prefixCls={prefixCls}
67 options={hourOptions.map(option => formatOption(option, disabledOptions))} 99 options={hourOptionsAdj.map(option => formatOption(option, disabledOptions))}
68 selectedIndex={hourOptions.indexOf(hour)} 100 selectedIndex={hourOptionsAdj.indexOf(hourAdj)}
69 type="hour" 101 type="hour"
70 onSelect={this.onItemChange} 102 onSelect={this.onItemChange}
71 onMouseEnter={this.onEnterSelectPanel.bind(this, 'hour')} 103 onMouseEnter={this.onEnterSelectPanel.bind(this, 'hour')}
@@ -113,6 +145,35 @@ const Combobox = React.createClass({
113 ); 145 );
114 }, 146 },
115 147
148 getAMPMSelect() {
149 const { prefixCls, use12Hours, format } = this.props;
150 if (!use12Hours) {
151 return null;
152 }
153
154 const AMPMOptions = ['am', 'pm'] // If format has A char, then we should uppercase AM/PM
155 .map(c => format.match(/\sA/) ? c.toUpperCase() : c)
156 .map(c => ({ value: c }));
157
158 const selected = this.isAM() ? 0 : 1;
159
160 return (
161 <Select
162 prefixCls={prefixCls}
163 options={AMPMOptions}
164 selectedIndex={selected}
165 type="ampm"
166 onSelect={this.onItemChange}
167 onMouseEnter={this.onEnterSelectPanel.bind(this, 'ampm')}
168 />
169 );
170 },
171
172 isAM() {
173 const { value } = this.props;
174 return value.hour() >= 0 && value.hour() < 12;
175 },
176
116 render() { 177 render() {
117 const { prefixCls, defaultOpenValue } = this.props; 178 const { prefixCls, defaultOpenValue } = this.props;
118 const value = this.props.value || defaultOpenValue; 179 const value = this.props.value || defaultOpenValue;
@@ -121,6 +182,7 @@ const Combobox = React.createClass({
121 {this.getHourSelect(value.hour())} 182 {this.getHourSelect(value.hour())}
122 {this.getMinuteSelect(value.minute())} 183 {this.getMinuteSelect(value.minute())}
123 {this.getSecondSelect(value.second())} 184 {this.getSecondSelect(value.second())}
185 {this.getAMPMSelect(value.hour())}
124 </div> 186 </div>
125 ); 187 );
126 }, 188 },
diff --git a/src/Panel.jsx b/src/Panel.jsx
index fddea1c..df128ff 100644
--- a/src/Panel.jsx
+++ b/src/Panel.jsx
@@ -37,6 +37,7 @@ const Panel = React.createClass({
37 showMinute: PropTypes.bool, 37 showMinute: PropTypes.bool,
38 showSecond: PropTypes.bool, 38 showSecond: PropTypes.bool,
39 onClear: PropTypes.func, 39 onClear: PropTypes.func,
40 use12Hours: PropTypes.bool,
40 addon: PropTypes.func, 41 addon: PropTypes.func,
41 }, 42 },
42 43
@@ -49,6 +50,7 @@ const Panel = React.createClass({
49 disabledMinutes: noop, 50 disabledMinutes: noop,
50 disabledSeconds: noop, 51 disabledSeconds: noop,
51 defaultOpenValue: moment(), 52 defaultOpenValue: moment(),
53 use12Hours: false,
52 addon: noop, 54 addon: noop,
53 }; 55 };
54 }, 56 },
@@ -90,7 +92,7 @@ const Panel = React.createClass({
90 const { 92 const {
91 prefixCls, className, placeholder, disabledHours, disabledMinutes, 93 prefixCls, className, placeholder, disabledHours, disabledMinutes,
92 disabledSeconds, hideDisabledOptions, allowEmpty, showHour, showMinute, showSecond, 94 disabledSeconds, hideDisabledOptions, allowEmpty, showHour, showMinute, showSecond,
93 format, defaultOpenValue, clearText, onEsc, addon, 95 format, defaultOpenValue, clearText, onEsc, addon, use12Hours,
94 } = this.props; 96 } = this.props;
95 const { 97 const {
96 value, currentSelectPanel, 98 value, currentSelectPanel,
@@ -140,6 +142,7 @@ const Panel = React.createClass({
140 disabledMinutes={disabledMinutes} 142 disabledMinutes={disabledMinutes}
141 disabledSeconds={disabledSeconds} 143 disabledSeconds={disabledSeconds}
142 onCurrentSelectPanelChange={this.onCurrentSelectPanelChange} 144 onCurrentSelectPanelChange={this.onCurrentSelectPanelChange}
145 use12Hours={use12Hours}
143 /> 146 />
144 {addon(this)} 147 {addon(this)}
145 </div> 148 </div>
diff --git a/src/Select.jsx b/src/Select.jsx
index 238a776..5733b1a 100644
--- a/src/Select.jsx
+++ b/src/Select.jsx
@@ -58,7 +58,7 @@ const Select = React.createClass({
58 }); 58 });
59 let onclick = null; 59 let onclick = null;
60 if (!item.disabled) { 60 if (!item.disabled) {
61 onclick = this.onSelect.bind(this, +item.value); 61 onclick = this.onSelect.bind(this, item.value);
62 } 62 }
63 return (<li 63 return (<li
64 className={cls} 64 className={cls}
diff --git a/src/TimePicker.jsx b/src/TimePicker.jsx
index e9d6d15..7065333 100644
--- a/src/TimePicker.jsx
+++ b/src/TimePicker.jsx
@@ -43,6 +43,7 @@ const Picker = React.createClass({
43 addon: PropTypes.func, 43 addon: PropTypes.func,
44 name: PropTypes.string, 44 name: PropTypes.string,
45 autoComplete: PropTypes.string, 45 autoComplete: PropTypes.string,
46 use12Hours: PropTypes.bool,
46 }, 47 },
47 48
48 getDefaultProps() { 49 getDefaultProps() {
@@ -67,6 +68,7 @@ const Picker = React.createClass({
67 onOpen: noop, 68 onOpen: noop,
68 onClose: noop, 69 onClose: noop,
69 addon: noop, 70 addon: noop,
71 use12Hours: false,
70 }; 72 };
71 }, 73 },
72 74
@@ -126,10 +128,21 @@ const Picker = React.createClass({
126 }, 128 },
127 129
128 getFormat() { 130 getFormat() {
129 const { format, showHour, showMinute, showSecond } = this.props; 131 const { format, showHour, showMinute, showSecond, use12Hours } = this.props;
130 if (format) { 132 if (format) {
131 return format; 133 return format;
132 } 134 }
135
136 if (use12Hours) {
137 const fmtString = ([
138 showHour ? 'h' : '',
139 showMinute ? 'mm' : '',
140 showSecond ? 'ss' : '',
141 ].filter(item => !!item).join(':'));
142
143 return fmtString.concat(' a');
144 }
145
133 return [ 146 return [
134 showHour ? 'HH' : '', 147 showHour ? 'HH' : '',
135 showMinute ? 'mm' : '', 148 showMinute ? 'mm' : '',
@@ -142,7 +155,7 @@ const Picker = React.createClass({
142 prefixCls, placeholder, disabledHours, 155 prefixCls, placeholder, disabledHours,
143 disabledMinutes, disabledSeconds, hideDisabledOptions, 156 disabledMinutes, disabledSeconds, hideDisabledOptions,
144 allowEmpty, showHour, showMinute, showSecond, defaultOpenValue, clearText, 157 allowEmpty, showHour, showMinute, showSecond, defaultOpenValue, clearText,
145 addon, 158 addon, use12Hours,
146 } = this.props; 159 } = this.props;
147 return ( 160 return (
148 <Panel 161 <Panel
@@ -164,6 +177,7 @@ const Picker = React.createClass({
164 disabledMinutes={disabledMinutes} 177 disabledMinutes={disabledMinutes}
165 disabledSeconds={disabledSeconds} 178 disabledSeconds={disabledSeconds}
166 hideDisabledOptions={hideDisabledOptions} 179 hideDisabledOptions={hideDisabledOptions}
180 use12Hours={use12Hours}
167 addon={addon} 181 addon={addon}
168 /> 182 />
169 ); 183 );
diff --git a/tests/Select.spec.jsx b/tests/Select.spec.jsx
index e6d32dd..fd2ec32 100644
--- a/tests/Select.spec.jsx
+++ b/tests/Select.spec.jsx
@@ -315,4 +315,180 @@ describe('Select', () => {
315 }); 315 });
316 }); 316 });
317 }); 317 });
318
319
320 describe('select in 12 hours mode', () => {
321 it('renders correctly', (done) => {
322 const picker = renderPicker({
323 use12Hours: true,
324 defaultValue: moment().hour(14).minute(0).second(0),
325 showSecond: false,
326 format: undefined,
327 });
328 expect(picker.state.open).not.to.be.ok();
329 const input = TestUtils.scryRenderedDOMComponentsWithClass(picker,
330 'rc-time-picker-input')[0];
331 let selector;
332 async.series([(next) => {
333 expect(picker.state.open).to.be(false);
334
335 Simulate.click(input);
336 setTimeout(next, 100);
337 }, (next) => {
338 expect(picker.state.open).to.be(true);
339 selector = TestUtils.scryRenderedDOMComponentsWithClass(picker.panelInstance,
340 'rc-time-picker-panel-select');
341 expect((input).value).to.be('2:00 pm');
342
343 setTimeout(next, 100);
344 }, (next) => {
345 expect(selector.length).to.be(3);
346
347 next();
348 }], () => {
349 done();
350 });
351 });
352
353
354 it('renders 12am correctly', (done) => {
355 const picker = renderPicker({
356 use12Hours: true,
357 defaultValue: moment().hour(0).minute(0).second(0),
358 showSecond: false,
359 format: undefined,
360 });
361 expect(picker.state.open).not.to.be.ok();
362 const input = TestUtils.scryRenderedDOMComponentsWithClass(picker,
363 'rc-time-picker-input')[0];
364 let selector;
365 async.series([(next) => {
366 expect(picker.state.open).to.be(false);
367
368 Simulate.click(input);
369 setTimeout(next, 100);
370 }, (next) => {
371 expect(picker.state.open).to.be(true);
372 selector = TestUtils.scryRenderedDOMComponentsWithClass(picker.panelInstance,
373 'rc-time-picker-panel-select');
374 setTimeout(next, 100);
375 }, (next) => {
376 expect(selector.length).to.be(3);
377
378 next();
379 }], () => {
380 done();
381 });
382 });
383
384
385 it('renders 5am correctly', (done) => {
386 const picker = renderPicker({
387 use12Hours: true,
388 defaultValue: moment().hour(0).minute(0).second(0),
389 showSecond: false,
390 format: undefined,
391 });
392 expect(picker.state.open).not.to.be.ok();
393 const input = TestUtils.scryRenderedDOMComponentsWithClass(picker,
394 'rc-time-picker-input')[0];
395 let selector;
396 async.series([(next) => {
397 expect(picker.state.open).to.be(false);
398
399 Simulate.click(input);
400 setTimeout(next, 100);
401 }, (next) => {
402 expect(picker.state.open).to.be(true);
403 selector = TestUtils.scryRenderedDOMComponentsWithClass(picker.panelInstance,
404 'rc-time-picker-panel-select')[0];
405 expect((input).value).to.be('12:00 am');
406 const option = selector.getElementsByTagName('li')[3];
407 Simulate.click(option);
408 setTimeout(next, 100);
409 }, (next) => {
410 expect((input).value).to.be('3:00 am');
411 next();
412 }], () => {
413 done();
414 });
415 });
416
417
418 it('renders 12am/pm correctly', (done) => {
419 const picker = renderPicker({
420 use12Hours: true,
421 defaultValue: moment().hour(0).minute(0).second(0),
422 showSecond: false,
423 format: undefined,
424 });
425 expect(picker.state.open).not.to.be.ok();
426 const input = TestUtils.scryRenderedDOMComponentsWithClass(picker,
427 'rc-time-picker-input')[0];
428 let selector;
429 async.series([(next) => {
430 expect(picker.state.open).to.be(false);
431
432 Simulate.click(input);
433 setTimeout(next, 100);
434 }, (next) => {
435 expect(picker.state.open).to.be(true);
436 selector = TestUtils.scryRenderedDOMComponentsWithClass(picker.panelInstance,
437 'rc-time-picker-panel-select')[2];
438 expect((input).value).to.be('12:00 am');
439 const option = selector.getElementsByTagName('li')[1];
440 Simulate.click(option);
441 setTimeout(next, 200);
442 }, (next) => {
443 expect((input).value).to.be('12:00 pm');
444 next();
445 }, (next) => {
446 Simulate.click(selector.getElementsByTagName('li')[0]);
447 setTimeout(next, 200);
448 }, (next) => {
449 expect((input).value).to.be('12:00 am');
450 next();
451 }], () => {
452 done();
453 });
454 });
455
456 it('renders uppercase AM correctly', (done) => {
457 const picker = renderPicker({
458 use12Hours: true,
459 defaultValue: moment().hour(0).minute(0).second(0),
460 showSecond: false,
461 format: 'h:mm A',
462 });
463 expect(picker.state.open).not.to.be.ok();
464 const input = TestUtils.scryRenderedDOMComponentsWithClass(picker,
465 'rc-time-picker-input')[0];
466 let selector;
467 async.series([(next) => {
468 expect(picker.state.open).to.be(false);
469
470 Simulate.click(input);
471 setTimeout(next, 100);
472 }, (next) => {
473 expect(picker.state.open).to.be(true);
474 selector = TestUtils.scryRenderedDOMComponentsWithClass(picker.panelInstance,
475 'rc-time-picker-panel-select')[2];
476 expect((input).value).to.be('12:00 AM');
477 const option = selector.getElementsByTagName('li')[1];
478 Simulate.click(option);
479 setTimeout(next, 200);
480 }, (next) => {
481 expect((input).value).to.be('12:00 PM');
482 next();
483 }, (next) => {
484 Simulate.click(selector.getElementsByTagName('li')[0]);
485 setTimeout(next, 200);
486 }, (next) => {
487 expect((input).value).to.be('12:00 AM');
488 next();
489 }], () => {
490 done();
491 });
492 });
493 });
318}); 494});