aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/module
diff options
context:
space:
mode:
Diffstat (limited to 'src/module')
-rw-r--r--src/module/Combobox.jsx95
-rw-r--r--src/module/Header.jsx139
-rw-r--r--src/module/Select.jsx88
3 files changed, 322 insertions, 0 deletions
diff --git a/src/module/Combobox.jsx b/src/module/Combobox.jsx
new file mode 100644
index 0000000..e6fe5ed
--- /dev/null
+++ b/src/module/Combobox.jsx
@@ -0,0 +1,95 @@
1import React, {PropTypes} from 'react';
2import Select from './Select';
3
4const formatOption = (option) => {
5 if (option < 10) {
6 return `0${option}`;
7 }
8 return `${option}`;
9};
10
11const Combobox = React.createClass({
12 propTypes: {
13 formatter: PropTypes.object,
14 prefixCls: PropTypes.string,
15 value: PropTypes.object,
16 onChange: PropTypes.func,
17 showHour: PropTypes.bool,
18 showSecond: PropTypes.bool,
19 hourOptions: PropTypes.array,
20 minuteOptions: PropTypes.array,
21 secondOptions: PropTypes.array,
22 },
23
24 onItemChange(type, itemValue) {
25 const { value, onChange } = this.props;
26 let index = 4;
27 if (type === 'minute') {
28 index = 5;
29 } else if (type === 'second') {
30 index = 6;
31 }
32 value.fields[index] = itemValue;
33 onChange(value);
34 },
35
36 getHourSelect(hour) {
37 const { prefixCls, hourOptions, showHour } = this.props;
38 if (!showHour) {
39 return null;
40 }
41 return (
42 <Select
43 prefixCls={prefixCls}
44 options={hourOptions.map(option => formatOption(option))}
45 selectedIndex={hourOptions.indexOf(hour)}
46 type="hour"
47 onSelect={this.onItemChange}
48 />
49 );
50 },
51
52 getMinuteSelect(minute) {
53 const { prefixCls, minuteOptions } = this.props;
54 return (
55 <Select
56 prefixCls={prefixCls}
57 options={minuteOptions.map(option => formatOption(option))}
58 selectedIndex={minuteOptions.indexOf(minute)}
59 type="minute"
60 onSelect={this.onItemChange}
61 />
62 );
63 },
64
65 getSectionSelect(second) {
66 const { prefixCls, secondOptions, showSecond } = this.props;
67 if (!showSecond) {
68 return null;
69 }
70 return (
71 <Select
72 prefixCls={prefixCls}
73 options={secondOptions.map(option => formatOption(option))}
74 selectedIndex={secondOptions.indexOf(second)}
75 type="second"
76 onSelect={this.onItemChange}
77 />
78 );
79 },
80
81 render() {
82 const { prefixCls, value } = this.props;
83 const timeFields = value.fields;
84
85 return (
86 <div className={`${prefixCls}-combobox`}>
87 {this.getHourSelect(timeFields[4])}
88 {this.getMinuteSelect(timeFields[5])}
89 {this.getSectionSelect(timeFields[6])}
90 </div>
91 );
92 },
93});
94
95export default Combobox;
diff --git a/src/module/Header.jsx b/src/module/Header.jsx
new file mode 100644
index 0000000..f7e443f
--- /dev/null
+++ b/src/module/Header.jsx
@@ -0,0 +1,139 @@
1import React, {PropTypes} from 'react';
2
3const Header = React.createClass({
4 propTypes: {
5 formatter: PropTypes.object,
6 prefixCls: PropTypes.string,
7 gregorianTimepickerLocale: PropTypes.object,
8 locale: PropTypes.object,
9 disabledDate: PropTypes.func,
10 placeholder: PropTypes.string,
11 value: PropTypes.object,
12 hourOptions: PropTypes.array,
13 minuteOptions: PropTypes.array,
14 secondOptions: PropTypes.array,
15 onChange: PropTypes.func,
16 onClear: PropTypes.func,
17 showClear: PropTypes.bool,
18 },
19
20 getInitialState() {
21 const value = this.props.value;
22 return {
23 str: value && this.props.formatter.format(value) || '',
24 invalid: false,
25 };
26 },
27
28 componentWillReceiveProps(nextProps) {
29 const value = this.formatValue(nextProps.value);
30 this.setState({
31 str: value && nextProps.formatter.format(value) || '',
32 invalid: false,
33 });
34 },
35
36 onInputChange(event) {
37 const str = event.target.value;
38 this.setState({
39 str,
40 });
41 let value = null;
42 const {formatter, gregorianTimepickerLocale, hourOptions, minuteOptions, secondOptions, onChange} = this.props;
43
44 if (str) {
45 const originalValue = this.props.value;
46 try {
47 value = formatter.parse(str, {
48 locale: gregorianTimepickerLocale,
49 obeyCount: true,
50 });
51 value = this.formatValue(value);
52 } catch (ex) {
53 this.setState({
54 invalid: true,
55 });
56 return;
57 }
58
59 if (value) {
60 if (
61 hourOptions.indexOf(value.fields[4]) < 0 ||
62 minuteOptions.indexOf(value.fields[5]) < 0 ||
63 secondOptions.indexOf(value.fields[6]) < 0
64 ) {
65 this.setState({
66 invalid: true,
67 });
68 return;
69 }
70
71 if (originalValue && value) {
72 if (
73 originalValue.fields[4] !== value.fields[4] ||
74 originalValue.fields[5] !== value.fields[5] ||
75 originalValue.fields[6] !== value.fields[6]
76 ) {
77 onChange(value);
78 }
79 } else if (originalValue !== value) {
80 onChange(value);
81 }
82 } else {
83 this.setState({
84 invalid: true,
85 });
86 return;
87 }
88 } else {
89 onChange(null);
90 }
91
92 this.setState({
93 invalid: false,
94 });
95 },
96
97 onClear() {
98 this.setState({str: ''});
99 this.props.onClear();
100 },
101
102 getClearButton() {
103 const { locale, prefixCls, showClear } = this.props;
104 if (!showClear) {
105 return null;
106 }
107 return <a className={`${prefixCls}-clear-btn`} role="button" title={locale.clear} onMouseDown={this.onClear} />;
108 },
109
110 getInput() {
111 const { prefixCls, placeholder } = this.props;
112 const { invalid, str } = this.state;
113 const invalidClass = invalid ? `${prefixCls}-input-invalid` : '';
114 return <input className={`${prefixCls}-input ${invalidClass}`} value={str} placeholder={placeholder} onChange={this.onInputChange} />;
115 },
116
117 formatValue(value) {
118 const newValue = this.props.value.clone();
119 if (!value) {
120 return newValue;
121 }
122 newValue.fields[4] = value.fields[4];
123 newValue.fields[5] = value.fields[5];
124 newValue.fields[6] = value.fields[6];
125 return newValue;
126 },
127
128 render() {
129 const { prefixCls } = this.props;
130 return (
131 <div className={`${prefixCls}-input-wrap`}>
132 {this.getInput()}
133 {this.getClearButton()}
134 </div>
135 );
136 },
137});
138
139export default Header;
diff --git a/src/module/Select.jsx b/src/module/Select.jsx
new file mode 100644
index 0000000..2b69623
--- /dev/null
+++ b/src/module/Select.jsx
@@ -0,0 +1,88 @@
1import React, {PropTypes} from 'react';
2import ReactDom from 'react-dom';
3import classnames from 'classnames';
4
5const scrollTo = (element, to, duration) => {
6 // jump to target if duration zero
7 if (duration <= 0) {
8 element.scrollTop = to;
9 return;
10 }
11 const difference = to - element.scrollTop;
12 const perTick = difference / duration * 10;
13
14 setTimeout(() => {
15 element.scrollTop = element.scrollTop + perTick;
16 if (element.scrollTop === to) return;
17 scrollTo(element, to, duration - 10);
18 }, 10);
19};
20
21const Select = React.createClass({
22 propTypes: {
23 prefixCls: PropTypes.string,
24 options: PropTypes.array,
25 selectedIndex: PropTypes.number,
26 type: PropTypes.string,
27 onSelect: PropTypes.func,
28 },
29
30 componentDidMount() {
31 // jump to selected option
32 this.scrollToSelected(0);
33 },
34
35 componentDidUpdate() {
36 // smooth scroll to selected option
37 this.scrollToSelected(200);
38 },
39
40 onSelect(event) {
41 // do nothing when select selected option
42 if (event.target.getAttribute('class') === 'selected') {
43 return;
44 }
45 // change combobox selection
46 const { onSelect, type } = this.props;
47 const value = parseInt(event.target.innerHTML, 10);
48 onSelect(type, value);
49 },
50
51 getOptions() {
52 const { options, selectedIndex } = this.props;
53 return options.map((item, index) => {
54 const cls = classnames({ selected: selectedIndex === index});
55 const ref = selectedIndex === index ? 'selected' : null;
56 return <li ref={ref} className={cls} key={index} onClick={this.onSelect}>{item}</li>;
57 });
58 },
59
60 scrollToSelected(duration) {
61 // move to selected item
62 const select = ReactDom.findDOMNode(this);
63 const list = ReactDom.findDOMNode(this.refs.list);
64 let index = this.props.selectedIndex - 2;
65 if (index < 0) {
66 index = 0;
67 }
68 const topOption = list.children[index];
69 const to = topOption.offsetTop - select.offsetTop;
70 scrollTo(select, to, duration);
71 },
72
73 render() {
74 if (this.props.options.length === 0) {
75 return null;
76 }
77
78 const { prefixCls } = this.props;
79
80 return (
81 <div className={`${prefixCls}-select`}>
82 <ul ref="list">{this.getOptions()}</ul>
83 </div>
84 );
85 },
86});
87
88export default Select;