diff options
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | assets/index.less | 11 | ||||
-rw-r--r-- | assets/index/Combobox.less | 4 | ||||
-rw-r--r-- | assets/index/Header.less | 7 | ||||
-rw-r--r-- | assets/index/Panel.less | 38 | ||||
-rw-r--r-- | assets/index/Picker.less | 42 | ||||
-rw-r--r-- | assets/index/Select.less | 12 | ||||
-rw-r--r-- | examples/pick-time.js | 13 | ||||
-rw-r--r-- | package.json | 7 | ||||
-rw-r--r-- | src/TimePicker.jsx | 134 | ||||
-rw-r--r-- | src/mixin/CommonMixin.js | 30 | ||||
-rw-r--r-- | src/module/Combobox.jsx | 24 | ||||
-rw-r--r-- | src/module/Header.jsx | 44 | ||||
-rw-r--r-- | src/module/Panel.jsx | 49 | ||||
-rw-r--r-- | src/module/Select.jsx | 19 |
15 files changed, 243 insertions, 201 deletions
@@ -31,16 +31,16 @@ API | |||
31 | | disabled | Boolean | false | whether picker is disabled | | 31 | | disabled | Boolean | false | whether picker is disabled | |
32 | | open | Boolean | false | current open state of picker. controlled prop | | 32 | | open | Boolean | false | current open state of picker. controlled prop | |
33 | | defaultValue | GregorianCalendar | null | default initial value | | 33 | | defaultValue | GregorianCalendar | null | default initial value | |
34 | | value | GregorianCalendar | null | current value | | 34 | | value | GregorianCalendar | null | current value | |
35 | | gregorianCalendarLocale | GregorianCalendar locale object | null | if value and defaultValue not set, you should set this to your locale | | ||
35 | | placeholder | String | '' | time input's placeholder | | 36 | | placeholder | String | '' | time input's placeholder | |
36 | | formatter | GregorianCalendarFormatter | HH:mm:ss or HH:mm or mm:ss | | | 37 | | showHour | Boolean | whether show hour | | |
38 | | showSecond | Boolean | whether show second | | | ||
39 | | formatter | String|GregorianCalendarFormatter | | | | ||
37 | | hourOptions | Array<String> | hour options | | | 40 | | hourOptions | Array<String> | hour options | | |
38 | | minuteOptions | Array<String> | minute options | | | 41 | | minuteOptions | Array<String> | minute options | | |
39 | | secondOptions | Array<String> | second options | | | 42 | | secondOptions | Array<String> | second options | | |
40 | | onChange | Function | null | called when select a different value | | 43 | | onChange | Function | null | called when select a different value | |
41 | | onOpen | Function | null | called when open picker | | ||
42 | | onClose | Function | null | called when close picker | | ||
43 | | inputClassName | String | '' | | | ||
44 | | placement | String | bottomLeft | one of ['left','right','top','bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'] | | 44 | | placement | String | bottomLeft | one of ['left','right','top','bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'] | |
45 | | transitionName | String | '' | | | 45 | | transitionName | String | '' | | |
46 | 46 | ||
diff --git a/assets/index.less b/assets/index.less index 36156cb..f4caf00 100644 --- a/assets/index.less +++ b/assets/index.less | |||
@@ -1,23 +1,14 @@ | |||
1 | @prefixClass: rc-time-picker; | 1 | @prefixClass: rc-time-picker; |
2 | 2 | ||
3 | .@{prefixClass} { | 3 | .@{prefixClass} { |
4 | display: inline; | 4 | display: inline-block; |
5 | box-sizing: border-box; | 5 | box-sizing: border-box; |
6 | * { | 6 | * { |
7 | box-sizing: border-box; | 7 | box-sizing: border-box; |
8 | } | 8 | } |
9 | } | 9 | } |
10 | 10 | ||
11 | @font-face { | ||
12 | font-family: 'anticon'; | ||
13 | src: url('//at.alicdn.com/t/font_1434092639_4910953.eot'); | ||
14 | /* IE9*/ | ||
15 | src: url('//at.alicdn.com/t/font_1434092639_4910953.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('//at.alicdn.com/t/font_1434092639_4910953.woff') format('woff'), /* chrome、firefox */ url('//at.alicdn.com/t/font_1434092639_4910953.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/ url('//at.alicdn.com/t/font_1434092639_4910953.svg#iconfont') format('svg'); | ||
16 | /* iOS 4.1- */ | ||
17 | } | ||
18 | |||
19 | @import "./index/Picker"; | 11 | @import "./index/Picker"; |
20 | @import "./index/Panel"; | 12 | @import "./index/Panel"; |
21 | @import "./index/Header"; | 13 | @import "./index/Header"; |
22 | @import "./index/Combobox"; | ||
23 | @import "./index/Select"; | 14 | @import "./index/Select"; |
diff --git a/assets/index/Combobox.less b/assets/index/Combobox.less deleted file mode 100644 index a796d0a..0000000 --- a/assets/index/Combobox.less +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | .@{prefixClass} { | ||
2 | &-combobox { | ||
3 | } | ||
4 | } | ||
diff --git a/assets/index/Header.less b/assets/index/Header.less index ac3d662..7ad3d9f 100644 --- a/assets/index/Header.less +++ b/assets/index/Header.less | |||
@@ -1,8 +1,7 @@ | |||
1 | .@{prefixClass} { | 1 | .@{prefixClass}-panel { |
2 | &-input { | 2 | &-input { |
3 | margin: 0; | 3 | margin: 0; |
4 | padding: 0; | 4 | padding: 0; |
5 | border: 0; | ||
6 | width: 100%; | 5 | width: 100%; |
7 | cursor: auto; | 6 | cursor: auto; |
8 | line-height: 1.5; | 7 | line-height: 1.5; |
@@ -48,7 +47,3 @@ | |||
48 | color: #666; | 47 | color: #666; |
49 | } | 48 | } |
50 | } | 49 | } |
51 | |||
52 | .narrow .@{prefixClass}-input-wrap { | ||
53 | max-width: 111px; | ||
54 | } | ||
diff --git a/assets/index/Panel.less b/assets/index/Panel.less index a11c4aa..be7914f 100644 --- a/assets/index/Panel.less +++ b/assets/index/Panel.less | |||
@@ -1,15 +1,25 @@ | |||
1 | .@{prefixClass}-panel-inner { | 1 | .@{prefixClass}-panel { |
2 | display: inline-block; | 2 | z-index: 1070; |
3 | position: relative; | 3 | width: 170px; |
4 | outline: none; | 4 | position: absolute; |
5 | border: 1px solid #ccc; | 5 | box-sizing: border-box; |
6 | list-style: none; | 6 | |
7 | font-size: 12px; | 7 | * { |
8 | text-align: left; | 8 | box-sizing: border-box; |
9 | background-color: #fff; | 9 | } |
10 | border-radius: 3px; | 10 | |
11 | box-shadow: 0 1px 5px #ccc; | 11 | &-inner { |
12 | background-clip: padding-box; | 12 | display: inline-block; |
13 | border: 1px solid #ccc; | 13 | position: relative; |
14 | line-height: 1.5; | 14 | outline: none; |
15 | list-style: none; | ||
16 | font-size: 12px; | ||
17 | text-align: left; | ||
18 | background-color: #fff; | ||
19 | border-radius: 3px; | ||
20 | box-shadow: 0 1px 5px #ccc; | ||
21 | background-clip: padding-box; | ||
22 | border: 1px solid #ccc; | ||
23 | line-height: 1.5; | ||
24 | } | ||
15 | } | 25 | } |
diff --git a/assets/index/Picker.less b/assets/index/Picker.less index dc1fbbd..76bf834 100644 --- a/assets/index/Picker.less +++ b/assets/index/Picker.less | |||
@@ -1,28 +1,18 @@ | |||
1 | .@{prefixClass}-panel { | 1 | .@{prefixClass} { |
2 | z-index: 1070; | 2 | &-input { |
3 | position: absolute; | 3 | width: 100%; |
4 | 4 | position: relative; | |
5 | &.slide-up-enter.slide-up-enter-active&-placement-topLeft, | 5 | display: inline-block; |
6 | &.slide-up-enter.slide-up-enter-active&-placement-topRight, | 6 | padding: 4px 7px; |
7 | &.slide-up-appear.slide-up-appear-active&-placement-topLeft, | 7 | height: 28px; |
8 | &.slide-up-appear.slide-up-appear-active&-placement-topRight { | 8 | cursor: text; |
9 | animation-name: antSlideDownIn; | 9 | font-size: 12px; |
10 | } | 10 | line-height: 1.5; |
11 | 11 | color: #666; | |
12 | &.slide-up-enter.slide-up-enter-active&-placement-bottomLeft, | 12 | background-color: #fff; |
13 | &.slide-up-enter.slide-up-enter-active&-placement-bottomRight, | 13 | background-image: none; |
14 | &.slide-up-appear.slide-up-appear-active&-placement-bottomLeft, | 14 | border: 1px solid #d9d9d9; |
15 | &.slide-up-appear.slide-up-appear-active&-placement-bottomRight { | 15 | border-radius: 6px; |
16 | animation-name: antSlideUpIn; | 16 | transition: border .2s cubic-bezier(0.645, 0.045, 0.355, 1), background .2s cubic-bezier(0.645, 0.045, 0.355, 1), box-shadow .2s cubic-bezier(0.645, 0.045, 0.355, 1); |
17 | } | ||
18 | |||
19 | &.slide-up-leave.slide-up-leave-active&-placement-topLeft, | ||
20 | &.slide-up-leave.slide-up-leave-active&-placement-topRight { | ||
21 | animation-name: antSlideDownOut; | ||
22 | } | ||
23 | |||
24 | &.slide-up-leave.slide-up-leave-active&-placement-bottomLeft, | ||
25 | &.slide-up-leave.slide-up-leave-active&-placement-bottomRight { | ||
26 | animation-name: antSlideUpOut; | ||
27 | } | 17 | } |
28 | } | 18 | } |
diff --git a/assets/index/Select.less b/assets/index/Select.less index 995d09e..e93e8f7 100644 --- a/assets/index/Select.less +++ b/assets/index/Select.less | |||
@@ -1,4 +1,4 @@ | |||
1 | .@{prefixClass}-select { | 1 | .@{prefixClass}-panel-select { |
2 | float: left; | 2 | float: left; |
3 | overflow-y:auto; | 3 | overflow-y:auto; |
4 | font-size: 12px; | 4 | font-size: 12px; |
@@ -38,13 +38,13 @@ | |||
38 | cursor: pointer; | 38 | cursor: pointer; |
39 | user-select: none; | 39 | user-select: none; |
40 | 40 | ||
41 | &.selected { | ||
42 | background: #edfaff; | ||
43 | color: #2db7f5; | ||
44 | } | ||
45 | |||
46 | &:hover { | 41 | &:hover { |
47 | background: #edfaff; | 42 | background: #edfaff; |
48 | } | 43 | } |
49 | } | 44 | } |
45 | |||
46 | &-option-selected { | ||
47 | background: #edfaff; | ||
48 | color: #2db7f5; | ||
49 | } | ||
50 | } | 50 | } |
diff --git a/examples/pick-time.js b/examples/pick-time.js index 70de23d..178b622 100644 --- a/examples/pick-time.js +++ b/examples/pick-time.js | |||
@@ -10,7 +10,10 @@ import zhCn from 'gregorian-calendar/lib/locale/zh_CN'; | |||
10 | import TimePicker from 'rc-time-picker'; | 10 | import TimePicker from 'rc-time-picker'; |
11 | import TimePickerLocale from 'rc-time-picker/src/locale/zh_CN'; | 11 | import TimePickerLocale from 'rc-time-picker/src/locale/zh_CN'; |
12 | 12 | ||
13 | const formatter = new DateTimeFormat('HH:mm:ss'); | 13 | var showSecond = true; |
14 | var str = showSecond ? 'HH:mm:ss' : 'HH:mm'; | ||
15 | |||
16 | const formatter = new DateTimeFormat(str); | ||
14 | 17 | ||
15 | const now = new GregorianCalendar(zhCn); | 18 | const now = new GregorianCalendar(zhCn); |
16 | now.setTime(Date.now()); | 19 | now.setTime(Date.now()); |
@@ -20,6 +23,12 @@ function onChange(value) { | |||
20 | } | 23 | } |
21 | 24 | ||
22 | ReactDom.render( | 25 | ReactDom.render( |
23 | <TimePicker formatter={formatter} locale={TimePickerLocale} defaultValue={now} onChange={onChange}/>, | 26 | <TimePicker formatter={formatter} locale={TimePickerLocale} |
27 | showSecond={showSecond} | ||
28 | defaultValue={now} | ||
29 | onChange={onChange}/>, | ||
24 | document.getElementById('__react-content') | 30 | document.getElementById('__react-content') |
25 | ); | 31 | ); |
32 | |||
33 | |||
34 | console.log(zhCn); \ No newline at end of file | ||
diff --git a/package.json b/package.json index 569661f..f9246c0 100644 --- a/package.json +++ b/package.json | |||
@@ -1,6 +1,6 @@ | |||
1 | { | 1 | { |
2 | "name": "rc-time-picker", | 2 | "name": "rc-time-picker", |
3 | "version": "0.7.1", | 3 | "version": "1.0.0-alpha1", |
4 | "description": "React TimePicker", | 4 | "description": "React TimePicker", |
5 | "keywords": [ | 5 | "keywords": [ |
6 | "react", | 6 | "react", |
@@ -57,10 +57,9 @@ | |||
57 | "lint" | 57 | "lint" |
58 | ], | 58 | ], |
59 | "dependencies": { | 59 | "dependencies": { |
60 | "classnames": "~2.2.0", | 60 | "classnames": "2.x", |
61 | "gregorian-calendar": "4.x", | 61 | "gregorian-calendar": "4.x", |
62 | "gregorian-calendar-format": "4.x", | 62 | "gregorian-calendar-format": "4.x", |
63 | "rc-trigger": "1.x", | 63 | "rc-trigger": "1.x" |
64 | "rc-util": "2.x" | ||
65 | } | 64 | } |
66 | } | 65 | } |
diff --git a/src/TimePicker.jsx b/src/TimePicker.jsx index e82b46f..2694cb0 100644 --- a/src/TimePicker.jsx +++ b/src/TimePicker.jsx | |||
@@ -1,10 +1,10 @@ | |||
1 | import React, {PropTypes} from 'react'; | 1 | import React, {PropTypes} from 'react'; |
2 | import ReactDOM from 'react-dom'; | ||
3 | import Trigger from 'rc-trigger'; | 2 | import Trigger from 'rc-trigger'; |
4 | import {createChainedFunction} from 'rc-util'; | ||
5 | import Panel from './module/Panel'; | 3 | import Panel from './module/Panel'; |
6 | import placements from './util/placements'; | 4 | import placements from './util/placements'; |
7 | import CommonMixin from './mixin/CommonMixin'; | 5 | import CommonMixin from './mixin/CommonMixin'; |
6 | import {getFormatter} from './util/index'; | ||
7 | import defaultGregorianCalendarLocale from 'gregorian-calendar/lib/locale/en_US'; | ||
8 | 8 | ||
9 | function noop() { | 9 | function noop() { |
10 | } | 10 | } |
@@ -16,18 +16,23 @@ function refFn(field, component) { | |||
16 | const Picker = React.createClass({ | 16 | const Picker = React.createClass({ |
17 | propTypes: { | 17 | propTypes: { |
18 | prefixCls: PropTypes.string, | 18 | prefixCls: PropTypes.string, |
19 | inputClassName: PropTypes.string, | ||
20 | locale: PropTypes.object, | 19 | locale: PropTypes.object, |
21 | value: PropTypes.object, | 20 | value: PropTypes.object, |
22 | children: PropTypes.func, | ||
23 | disabled: PropTypes.bool, | 21 | disabled: PropTypes.bool, |
22 | allowEmpty: PropTypes.bool, | ||
24 | defaultValue: PropTypes.object, | 23 | defaultValue: PropTypes.object, |
25 | open: PropTypes.bool, | 24 | open: PropTypes.bool, |
25 | defaultOpen: PropTypes.bool, | ||
26 | align: PropTypes.object, | 26 | align: PropTypes.object, |
27 | placement: PropTypes.any, | 27 | placement: PropTypes.any, |
28 | transitionName: PropTypes.string, | 28 | transitionName: PropTypes.string, |
29 | getPopupContainer: PropTypes.func, | ||
30 | gregorianCalendarLocale: PropTypes.object, | ||
29 | placeholder: PropTypes.string, | 31 | placeholder: PropTypes.string, |
30 | formatter: PropTypes.object, | 32 | formatter: PropTypes.any, |
33 | showHour: PropTypes.bool, | ||
34 | style: PropTypes.object, | ||
35 | showSecond: PropTypes.bool, | ||
31 | hourOptions: PropTypes.array, | 36 | hourOptions: PropTypes.array, |
32 | minuteOptions: PropTypes.array, | 37 | minuteOptions: PropTypes.array, |
33 | secondOptions: PropTypes.array, | 38 | secondOptions: PropTypes.array, |
@@ -40,8 +45,13 @@ const Picker = React.createClass({ | |||
40 | 45 | ||
41 | getDefaultProps() { | 46 | getDefaultProps() { |
42 | return { | 47 | return { |
43 | open: false, | 48 | defaultOpen: false, |
49 | style: {}, | ||
50 | gregorianCalendarLocale: defaultGregorianCalendarLocale, | ||
44 | align: {}, | 51 | align: {}, |
52 | allowEmpty: true, | ||
53 | showHour: true, | ||
54 | showSecond: true, | ||
45 | placement: 'bottomLeft', | 55 | placement: 'bottomLeft', |
46 | onChange: noop, | 56 | onChange: noop, |
47 | onOpen: noop, | 57 | onOpen: noop, |
@@ -51,10 +61,10 @@ const Picker = React.createClass({ | |||
51 | 61 | ||
52 | getInitialState() { | 62 | getInitialState() { |
53 | this.savePanelRef = refFn.bind(this, 'panelInstance'); | 63 | this.savePanelRef = refFn.bind(this, 'panelInstance'); |
54 | const { open, defaultValue, value } = this.props; | 64 | const { defaultOpen, defaultValue, open = defaultOpen, value = defaultValue } = this.props; |
55 | return { | 65 | return { |
56 | open: open, | 66 | open, |
57 | value: value || defaultValue, | 67 | value, |
58 | }; | 68 | }; |
59 | }, | 69 | }, |
60 | 70 | ||
@@ -80,12 +90,18 @@ const Picker = React.createClass({ | |||
80 | }, | 90 | }, |
81 | 91 | ||
82 | onVisibleChange(open) { | 92 | onVisibleChange(open) { |
83 | this.setOpen(open, () => { | 93 | this.setOpen(open); |
84 | if (open) { | 94 | }, |
85 | ReactDOM.findDOMNode(this.refs.picker).blur(); | 95 | |
86 | ReactDOM.findDOMNode(this.panelInstance).focus(); | 96 | onEsc() { |
87 | } | 97 | this.setOpen(false); |
88 | }); | 98 | this.refs.picker.focus(); |
99 | }, | ||
100 | |||
101 | onKeyDown(e) { | ||
102 | if (e.keyCode === 40) { | ||
103 | this.setOpen(true); | ||
104 | } | ||
89 | }, | 105 | }, |
90 | 106 | ||
91 | setValue(value) { | 107 | setValue(value) { |
@@ -97,14 +113,60 @@ const Picker = React.createClass({ | |||
97 | this.props.onChange(value); | 113 | this.props.onChange(value); |
98 | }, | 114 | }, |
99 | 115 | ||
100 | getPanel() { | 116 | getFormatter() { |
101 | const { prefixCls, defaultValue, locale, formatter, placeholder, hourOptions, minuteOptions, secondOptions } = this.props; | 117 | const formatter = this.props.formatter; |
118 | const locale = this.props.locale; | ||
119 | if (formatter) { | ||
120 | if (formatter === this.lastFormatter) { | ||
121 | return this.normalFormatter; | ||
122 | } | ||
123 | this.normalFormatter = getFormatter(formatter, locale); | ||
124 | this.lastFormatter = formatter; | ||
125 | return this.normalFormatter; | ||
126 | } | ||
127 | if (!this.props.showSecond) { | ||
128 | if (!this.notShowSecondFormatter) { | ||
129 | this.notShowSecondFormatter = getFormatter('HH:mm', locale); | ||
130 | } | ||
131 | return this.notShowSecondFormatter; | ||
132 | } | ||
133 | if (!this.props.showHour) { | ||
134 | if (!this.notShowHourFormatter) { | ||
135 | this.notShowHourFormatter = getFormatter('mm:ss', locale); | ||
136 | } | ||
137 | return this.notShowHourFormatter; | ||
138 | } | ||
139 | if (!this.normalFormatter) { | ||
140 | this.normalFormatter = getFormatter('HH:mm:ss', locale); | ||
141 | } | ||
142 | return this.normalFormatter; | ||
143 | }, | ||
144 | |||
145 | getPanelElement() { | ||
146 | const { prefixCls, defaultValue, locale, placeholder, hourOptions, minuteOptions, secondOptions, allowEmpty, showHour, showSecond, gregorianCalendarLocale, value } = this.props; | ||
147 | let calendarLocale; | ||
148 | if (value) { | ||
149 | calendarLocale = value.locale; | ||
150 | } else if (defaultValue) { | ||
151 | calendarLocale = defaultValue.locale; | ||
152 | } else { | ||
153 | calendarLocale = gregorianCalendarLocale; | ||
154 | } | ||
102 | return ( | 155 | return ( |
103 | <Panel | 156 | <Panel |
104 | prefixCls={prefixCls} | 157 | prefixCls={`${prefixCls}-panel`} |
158 | ref={this.savePanelRef} | ||
159 | value={this.state.value} | ||
160 | onChange={this.onPanelChange} | ||
161 | gregorianCalendarLocale={calendarLocale} | ||
162 | onClear={this.onPanelClear} | ||
105 | defaultValue={defaultValue} | 163 | defaultValue={defaultValue} |
164 | showHour={showHour} | ||
165 | onEsc={this.onEsc} | ||
166 | showSecond={showSecond} | ||
106 | locale={locale} | 167 | locale={locale} |
107 | formatter={formatter} | 168 | allowEmpty={allowEmpty} |
169 | formatter={this.getFormatter()} | ||
108 | placeholder={placeholder} | 170 | placeholder={placeholder} |
109 | hourOptions={hourOptions} | 171 | hourOptions={hourOptions} |
110 | minuteOptions={minuteOptions} | 172 | minuteOptions={minuteOptions} |
@@ -113,26 +175,14 @@ const Picker = React.createClass({ | |||
113 | ); | 175 | ); |
114 | }, | 176 | }, |
115 | 177 | ||
116 | getPanelElement() { | ||
117 | const panel = this.getPanel(); | ||
118 | const extraProps = { | ||
119 | ref: this.savePanelRef, | ||
120 | value: this.state.value, | ||
121 | onChange: createChainedFunction(panel.props.onChange, this.onPanelChange), | ||
122 | onClear: createChainedFunction(panel.props.onClear, this.onPanelClear), | ||
123 | }; | ||
124 | |||
125 | return React.cloneElement(panel, extraProps); | ||
126 | }, | ||
127 | |||
128 | setOpen(open, callback) { | 178 | setOpen(open, callback) { |
129 | const {onOpen, onClose} = this.props; | 179 | const {onOpen, onClose} = this.props; |
130 | if (this.state.open !== open) { | 180 | if (this.state.open !== open) { |
131 | this.setState({ | 181 | this.setState({ |
132 | open: open, | 182 | open, |
133 | }, callback); | 183 | }, callback); |
134 | const event = { | 184 | const event = { |
135 | open: open, | 185 | open, |
136 | }; | 186 | }; |
137 | if (open) { | 187 | if (open) { |
138 | onOpen(event); | 188 | onOpen(event); |
@@ -143,25 +193,33 @@ const Picker = React.createClass({ | |||
143 | }, | 193 | }, |
144 | 194 | ||
145 | render() { | 195 | render() { |
146 | const { prefixCls, placeholder, placement, align, disabled, transitionName, formatter, inputClassName } = this.props; | 196 | const { prefixCls, placeholder, placement, align, disabled, transitionName, style, showHour, showSecond, getPopupContainer } = this.props; |
147 | const { open, value } = this.state; | 197 | const { open, value } = this.state; |
148 | 198 | let popupClassName; | |
199 | if (!showHour || !showSecond) { | ||
200 | popupClassName = `${prefixCls}-panel-narrow}`; | ||
201 | } | ||
149 | return ( | 202 | return ( |
150 | <Trigger | 203 | <Trigger |
151 | prefixCls={`${prefixCls}-panel`} | 204 | prefixCls={`${prefixCls}-panel`} |
205 | popupClassName={popupClassName} | ||
152 | popup={this.getPanelElement()} | 206 | popup={this.getPanelElement()} |
153 | popupAlign={align} | 207 | popupAlign={align} |
154 | builtinPlacements={placements} | 208 | builtinPlacements={placements} |
155 | popupPlacement={placement} | 209 | popupPlacement={placement} |
156 | action={disabled ? [] : ['click']} | 210 | action={disabled ? [] : ['click']} |
157 | destroyPopupOnHide | 211 | destroyPopupOnHide |
212 | getPopupContainer={getPopupContainer} | ||
158 | popupTransitionName={transitionName} | 213 | popupTransitionName={transitionName} |
159 | popupVisible={open} | 214 | popupVisible={open} |
160 | onPopupVisibleChange={this.onVisibleChange} | 215 | onPopupVisibleChange={this.onVisibleChange} |
161 | > | 216 | > |
162 | <span className={`${prefixCls}`}> | 217 | <span className={`${prefixCls}`} style={style}> |
163 | <input className={inputClassName} ref="picker" type="text" placeholder={placeholder} readOnly | 218 | <input className={`${prefixCls}-input`} |
164 | disabled={disabled} value={value && formatter.format(value)}/> | 219 | ref="picker" type="text" placeholder={placeholder} |
220 | readOnly | ||
221 | onKeyDown={this.onKeyDown} | ||
222 | disabled={disabled} value={value && this.getFormatter().format(value)}/> | ||
165 | <span className={`${prefixCls}-icon`}/> | 223 | <span className={`${prefixCls}-icon`}/> |
166 | </span> | 224 | </span> |
167 | </Trigger> | 225 | </Trigger> |
diff --git a/src/mixin/CommonMixin.js b/src/mixin/CommonMixin.js index 0e8ed32..a6893b8 100644 --- a/src/mixin/CommonMixin.js +++ b/src/mixin/CommonMixin.js | |||
@@ -1,6 +1,5 @@ | |||
1 | import {PropTypes} from 'react'; | 1 | import {PropTypes} from 'react'; |
2 | import enUs from '../locale/en_US'; | 2 | import enUs from '../locale/en_US'; |
3 | import {getFormatter} from '../util/index'; | ||
4 | 3 | ||
5 | export default { | 4 | export default { |
6 | propTypes: { | 5 | propTypes: { |
@@ -14,33 +13,4 @@ export default { | |||
14 | locale: enUs, | 13 | locale: enUs, |
15 | }; | 14 | }; |
16 | }, | 15 | }, |
17 | |||
18 | getFormatter() { | ||
19 | const formatter = this.props.formatter; | ||
20 | const locale = this.props.locale; | ||
21 | if (formatter) { | ||
22 | if (formatter === this.lastFormatter) { | ||
23 | return this.normalFormatter; | ||
24 | } | ||
25 | this.normalFormatter = getFormatter(formatter, locale); | ||
26 | this.lastFormatter = formatter; | ||
27 | return this.normalFormatter; | ||
28 | } | ||
29 | if (!this.showSecond) { | ||
30 | if (!this.notShowSecondFormatter) { | ||
31 | this.notShowSecondFormatter = getFormatter('HH:mm', locale); | ||
32 | } | ||
33 | return this.notShowSecondFormatter; | ||
34 | } | ||
35 | if (!this.showHour) { | ||
36 | if (!this.notShowHourFormatter) { | ||
37 | this.notShowHourFormatter = getFormatter('mm:ss', locale); | ||
38 | } | ||
39 | return this.notShowHourFormatter; | ||
40 | } | ||
41 | if (!this.normalFormatter) { | ||
42 | this.normalFormatter = getFormatter('HH:mm:ss', locale); | ||
43 | } | ||
44 | return this.normalFormatter; | ||
45 | }, | ||
46 | }; | 16 | }; |
diff --git a/src/module/Combobox.jsx b/src/module/Combobox.jsx index afce675..3dfd321 100644 --- a/src/module/Combobox.jsx +++ b/src/module/Combobox.jsx | |||
@@ -1,5 +1,6 @@ | |||
1 | import React, {PropTypes} from 'react'; | 1 | import React, {PropTypes} from 'react'; |
2 | import Select from './Select'; | 2 | import Select from './Select'; |
3 | import GregorianCalendar from 'gregorian-calendar'; | ||
3 | 4 | ||
4 | const formatOption = (option) => { | 5 | const formatOption = (option) => { |
5 | if (option < 10) { | 6 | if (option < 10) { |
@@ -15,6 +16,7 @@ const Combobox = React.createClass({ | |||
15 | value: PropTypes.object, | 16 | value: PropTypes.object, |
16 | onChange: PropTypes.func, | 17 | onChange: PropTypes.func, |
17 | showHour: PropTypes.bool, | 18 | showHour: PropTypes.bool, |
19 | gregorianCalendarLocale: PropTypes.object, | ||
18 | showSecond: PropTypes.bool, | 20 | showSecond: PropTypes.bool, |
19 | hourOptions: PropTypes.array, | 21 | hourOptions: PropTypes.array, |
20 | minuteOptions: PropTypes.array, | 22 | minuteOptions: PropTypes.array, |
@@ -22,7 +24,13 @@ const Combobox = React.createClass({ | |||
22 | }, | 24 | }, |
23 | 25 | ||
24 | onItemChange(type, itemValue) { | 26 | onItemChange(type, itemValue) { |
25 | const { value, onChange } = this.props; | 27 | const { onChange } = this.props; |
28 | let value = this.props.value; | ||
29 | if (value) { | ||
30 | value = value.clone(); | ||
31 | } else { | ||
32 | value = this.getNow().clone(); | ||
33 | } | ||
26 | if (type === 'hour') { | 34 | if (type === 'hour') { |
27 | value.setHourOfDay(itemValue); | 35 | value.setHourOfDay(itemValue); |
28 | } else if (type === 'minute') { | 36 | } else if (type === 'minute') { |
@@ -78,9 +86,19 @@ const Combobox = React.createClass({ | |||
78 | ); | 86 | ); |
79 | }, | 87 | }, |
80 | 88 | ||
81 | render() { | 89 | getNow() { |
82 | const { prefixCls, value } = this.props; | 90 | if (this.showNow) { |
91 | return this.showNow; | ||
92 | } | ||
93 | const value = new GregorianCalendar(this.props.gregorianCalendarLocale); | ||
94 | value.setTime(Date.now()); | ||
95 | this.showNow = value; | ||
96 | return value; | ||
97 | }, | ||
83 | 98 | ||
99 | render() { | ||
100 | const { prefixCls } = this.props; | ||
101 | const value = this.props.value || this.getNow(); | ||
84 | return ( | 102 | return ( |
85 | <div className={`${prefixCls}-combobox`}> | 103 | <div className={`${prefixCls}-combobox`}> |
86 | {this.getHourSelect(value.getHourOfDay())} | 104 | {this.getHourSelect(value.getHourOfDay())} |
diff --git a/src/module/Header.jsx b/src/module/Header.jsx index b65fd25..962328c 100644 --- a/src/module/Header.jsx +++ b/src/module/Header.jsx | |||
@@ -4,7 +4,7 @@ const Header = React.createClass({ | |||
4 | propTypes: { | 4 | propTypes: { |
5 | formatter: PropTypes.object, | 5 | formatter: PropTypes.object, |
6 | prefixCls: PropTypes.string, | 6 | prefixCls: PropTypes.string, |
7 | gregorianTimePickerLocale: PropTypes.object, | 7 | gregorianCalendarLocale: PropTypes.object, |
8 | locale: PropTypes.object, | 8 | locale: PropTypes.object, |
9 | disabledDate: PropTypes.func, | 9 | disabledDate: PropTypes.func, |
10 | placeholder: PropTypes.string, | 10 | placeholder: PropTypes.string, |
@@ -14,7 +14,8 @@ const Header = React.createClass({ | |||
14 | secondOptions: PropTypes.array, | 14 | secondOptions: PropTypes.array, |
15 | onChange: PropTypes.func, | 15 | onChange: PropTypes.func, |
16 | onClear: PropTypes.func, | 16 | onClear: PropTypes.func, |
17 | showClear: PropTypes.bool, | 17 | onEsc: PropTypes.func, |
18 | allowEmpty: PropTypes.bool, | ||
18 | }, | 19 | }, |
19 | 20 | ||
20 | getInitialState() { | 21 | getInitialState() { |
@@ -25,6 +26,12 @@ const Header = React.createClass({ | |||
25 | }; | 26 | }; |
26 | }, | 27 | }, |
27 | 28 | ||
29 | componentDidMount() { | ||
30 | this.timer = setTimeout(() => { | ||
31 | this.refs.input.focus(); | ||
32 | }, 0); | ||
33 | }, | ||
34 | |||
28 | componentWillReceiveProps(nextProps) { | 35 | componentWillReceiveProps(nextProps) { |
29 | const value = nextProps.value; | 36 | const value = nextProps.value; |
30 | this.setState({ | 37 | this.setState({ |
@@ -33,19 +40,23 @@ const Header = React.createClass({ | |||
33 | }); | 40 | }); |
34 | }, | 41 | }, |
35 | 42 | ||
43 | componentWillUnmount() { | ||
44 | clearTimeout(this.timer); | ||
45 | }, | ||
46 | |||
36 | onInputChange(event) { | 47 | onInputChange(event) { |
37 | const str = event.target.value; | 48 | const str = event.target.value; |
38 | this.setState({ | 49 | this.setState({ |
39 | str, | 50 | str, |
40 | }); | 51 | }); |
41 | let value = null; | 52 | let value = null; |
42 | const {formatter, gregorianTimePickerLocale, hourOptions, minuteOptions, secondOptions, onChange} = this.props; | 53 | const {formatter, gregorianCalendarLocale, hourOptions, minuteOptions, secondOptions, onChange, allowEmpty} = this.props; |
43 | 54 | ||
44 | if (str) { | 55 | if (str) { |
45 | const originalValue = this.props.value; | 56 | const originalValue = this.props.value; |
46 | try { | 57 | try { |
47 | value = formatter.parse(str, { | 58 | value = formatter.parse(str, { |
48 | locale: gregorianTimePickerLocale, | 59 | locale: gregorianCalendarLocale, |
49 | obeyCount: true, | 60 | obeyCount: true, |
50 | }); | 61 | }); |
51 | } catch (ex) { | 62 | } catch (ex) { |
@@ -84,8 +95,13 @@ const Header = React.createClass({ | |||
84 | }); | 95 | }); |
85 | return; | 96 | return; |
86 | } | 97 | } |
87 | } else { | 98 | } else if (allowEmpty) { |
88 | onChange(null); | 99 | onChange(null); |
100 | } else { | ||
101 | this.setState({ | ||
102 | invalid: true, | ||
103 | }); | ||
104 | return; | ||
89 | } | 105 | } |
90 | 106 | ||
91 | this.setState({ | 107 | this.setState({ |
@@ -93,24 +109,34 @@ const Header = React.createClass({ | |||
93 | }); | 109 | }); |
94 | }, | 110 | }, |
95 | 111 | ||
112 | onKeyDown(e) { | ||
113 | if (e.keyCode === 27) { | ||
114 | this.props.onEsc(); | ||
115 | } | ||
116 | }, | ||
117 | |||
96 | onClear() { | 118 | onClear() { |
97 | this.setState({str: ''}); | 119 | this.setState({str: ''}); |
98 | this.props.onClear(); | 120 | this.props.onClear(); |
99 | }, | 121 | }, |
100 | 122 | ||
101 | getClearButton() { | 123 | getClearButton() { |
102 | const { locale, prefixCls, showClear } = this.props; | 124 | const { locale, prefixCls, allowEmpty } = this.props; |
103 | if (!showClear) { | 125 | if (!allowEmpty) { |
104 | return null; | 126 | return null; |
105 | } | 127 | } |
106 | return <a className={`${prefixCls}-clear-btn`} role="button" title={locale.clear} onMouseDown={this.onClear} />; | 128 | return <a className={`${prefixCls}-clear-btn`} role="button" title={locale.clear} onMouseDown={this.onClear}/>; |
107 | }, | 129 | }, |
108 | 130 | ||
109 | getInput() { | 131 | getInput() { |
110 | const { prefixCls, placeholder } = this.props; | 132 | const { prefixCls, placeholder } = this.props; |
111 | const { invalid, str } = this.state; | 133 | const { invalid, str } = this.state; |
112 | const invalidClass = invalid ? `${prefixCls}-input-invalid` : ''; | 134 | const invalidClass = invalid ? `${prefixCls}-input-invalid` : ''; |
113 | return <input className={`${prefixCls}-input ${invalidClass}`} value={str} placeholder={placeholder} onChange={this.onInputChange} />; | 135 | return (<input className={`${prefixCls}-input ${invalidClass}`} |
136 | ref="input" | ||
137 | onKeyDown={this.onKeyDown} | ||
138 | value={str} | ||
139 | placeholder={placeholder} onChange={this.onInputChange}/>); | ||
114 | }, | 140 | }, |
115 | 141 | ||
116 | render() { | 142 | render() { |
diff --git a/src/module/Panel.jsx b/src/module/Panel.jsx index e372774..b91c6fe 100644 --- a/src/module/Panel.jsx +++ b/src/module/Panel.jsx | |||
@@ -1,8 +1,4 @@ | |||
1 | import React, {PropTypes} from 'react'; | 1 | import React, {PropTypes} from 'react'; |
2 | import classnames from 'classnames'; | ||
3 | import GregorianCalendar from 'gregorian-calendar'; | ||
4 | import zhCn from 'gregorian-calendar/lib/locale/zh_CN'; | ||
5 | |||
6 | import CommonMixin from '../mixin/CommonMixin'; | 2 | import CommonMixin from '../mixin/CommonMixin'; |
7 | import Header from './Header'; | 3 | import Header from './Header'; |
8 | import Combobox from './Combobox'; | 4 | import Combobox from './Combobox'; |
@@ -22,11 +18,16 @@ const Panel = React.createClass({ | |||
22 | value: PropTypes.object, | 18 | value: PropTypes.object, |
23 | locale: PropTypes.object, | 19 | locale: PropTypes.object, |
24 | placeholder: PropTypes.string, | 20 | placeholder: PropTypes.string, |
21 | gregorianCalendarLocale: PropTypes.object, | ||
25 | formatter: PropTypes.object, | 22 | formatter: PropTypes.object, |
26 | hourOptions: PropTypes.array, | 23 | hourOptions: PropTypes.array, |
27 | minuteOptions: PropTypes.array, | 24 | minuteOptions: PropTypes.array, |
28 | secondOptions: PropTypes.array, | 25 | secondOptions: PropTypes.array, |
29 | onChange: PropTypes.func, | 26 | onChange: PropTypes.func, |
27 | onEsc: PropTypes.func, | ||
28 | allowEmpty: PropTypes.bool, | ||
29 | showHour: PropTypes.bool, | ||
30 | showSecond: PropTypes.bool, | ||
30 | onClear: PropTypes.func, | 31 | onClear: PropTypes.func, |
31 | }, | 32 | }, |
32 | 33 | ||
@@ -43,26 +44,11 @@ const Panel = React.createClass({ | |||
43 | }, | 44 | }, |
44 | 45 | ||
45 | getInitialState() { | 46 | getInitialState() { |
46 | let value = this.props.value; | ||
47 | if (!value) { | ||
48 | value = new GregorianCalendar(zhCn); | ||
49 | value.setTime(Date.now()); | ||
50 | } | ||
51 | return { | 47 | return { |
52 | value, | 48 | value: this.props.value, |
53 | }; | 49 | }; |
54 | }, | 50 | }, |
55 | 51 | ||
56 | componentWillMount() { | ||
57 | const formatter = this.props.formatter; | ||
58 | const pattern = formatter.originalPattern; | ||
59 | if (pattern === 'HH:mm') { | ||
60 | this.showSecond = false; | ||
61 | } else if (pattern === 'mm:ss') { | ||
62 | this.showHour = false; | ||
63 | } | ||
64 | }, | ||
65 | |||
66 | componentWillReceiveProps(nextProps) { | 52 | componentWillReceiveProps(nextProps) { |
67 | const value = nextProps.value; | 53 | const value = nextProps.value; |
68 | if (value) { | 54 | if (value) { |
@@ -81,37 +67,34 @@ const Panel = React.createClass({ | |||
81 | this.props.onClear(); | 67 | this.props.onClear(); |
82 | }, | 68 | }, |
83 | 69 | ||
84 | showHour: true, | ||
85 | showSecond: true, | ||
86 | |||
87 | render() { | 70 | render() { |
88 | const { locale, prefixCls, placeholder, hourOptions, minuteOptions, secondOptions } = this.props; | 71 | const { locale, prefixCls, placeholder, hourOptions, minuteOptions, secondOptions, allowEmpty, showHour, showSecond, formatter, gregorianCalendarLocale } = this.props; |
89 | const value = this.state.value; | 72 | const value = this.state.value; |
90 | const cls = classnames({ 'narrow': !this.showHour || !this.showSecond }); | ||
91 | |||
92 | return ( | 73 | return ( |
93 | <div className={`${prefixCls}-panel-inner ${cls}`}> | 74 | <div className={`${prefixCls}-inner`}> |
94 | <Header | 75 | <Header |
95 | prefixCls={prefixCls} | 76 | prefixCls={prefixCls} |
96 | gregorianTimePickerLocale={value.locale} | 77 | gregorianCalendarLocale={gregorianCalendarLocale} |
97 | locale={locale} | 78 | locale={locale} |
98 | value={value} | 79 | value={value} |
99 | formatter={this.getFormatter()} | 80 | onEsc={this.props.onEsc} |
81 | formatter={formatter} | ||
100 | placeholder={placeholder} | 82 | placeholder={placeholder} |
101 | hourOptions={hourOptions} | 83 | hourOptions={hourOptions} |
102 | minuteOptions={minuteOptions} | 84 | minuteOptions={minuteOptions} |
103 | secondOptions={secondOptions} | 85 | secondOptions={secondOptions} |
104 | onChange={this.onChange} | 86 | onChange={this.onChange} |
105 | onClear={this.onClear} | 87 | onClear={this.onClear} |
106 | showClear | 88 | allowEmpty={allowEmpty} |
107 | /> | 89 | /> |
108 | <Combobox | 90 | <Combobox |
109 | prefixCls={prefixCls} | 91 | prefixCls={prefixCls} |
110 | value={value} | 92 | value={value} |
111 | formatter={this.getFormatter()} | 93 | gregorianCalendarLocale={gregorianCalendarLocale} |
94 | formatter={formatter} | ||
112 | onChange={this.onChange} | 95 | onChange={this.onChange} |
113 | showHour={this.showHour} | 96 | showHour={showHour} |
114 | showSecond={this.showSecond} | 97 | showSecond={showSecond} |
115 | hourOptions={hourOptions} | 98 | hourOptions={hourOptions} |
116 | minuteOptions={minuteOptions} | 99 | minuteOptions={minuteOptions} |
117 | secondOptions={secondOptions} | 100 | secondOptions={secondOptions} |
diff --git a/src/module/Select.jsx b/src/module/Select.jsx index 2b69623..be4c025 100644 --- a/src/module/Select.jsx +++ b/src/module/Select.jsx | |||
@@ -22,6 +22,7 @@ const Select = React.createClass({ | |||
22 | propTypes: { | 22 | propTypes: { |
23 | prefixCls: PropTypes.string, | 23 | prefixCls: PropTypes.string, |
24 | options: PropTypes.array, | 24 | options: PropTypes.array, |
25 | gregorianCalendarLocale: PropTypes.object, | ||
25 | selectedIndex: PropTypes.number, | 26 | selectedIndex: PropTypes.number, |
26 | type: PropTypes.string, | 27 | type: PropTypes.string, |
27 | onSelect: PropTypes.func, | 28 | onSelect: PropTypes.func, |
@@ -37,23 +38,19 @@ const Select = React.createClass({ | |||
37 | this.scrollToSelected(200); | 38 | this.scrollToSelected(200); |
38 | }, | 39 | }, |
39 | 40 | ||
40 | onSelect(event) { | 41 | onSelect(value) { |
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; | 42 | const { onSelect, type } = this.props; |
47 | const value = parseInt(event.target.innerHTML, 10); | ||
48 | onSelect(type, value); | 43 | onSelect(type, value); |
49 | }, | 44 | }, |
50 | 45 | ||
51 | getOptions() { | 46 | getOptions() { |
52 | const { options, selectedIndex } = this.props; | 47 | const { options, selectedIndex, prefixCls } = this.props; |
53 | return options.map((item, index) => { | 48 | return options.map((item, index) => { |
54 | const cls = classnames({ selected: selectedIndex === index}); | 49 | const selected = selectedIndex === index; |
55 | const ref = selectedIndex === index ? 'selected' : null; | 50 | const cls = classnames({ |
56 | return <li ref={ref} className={cls} key={index} onClick={this.onSelect}>{item}</li>; | 51 | [`${prefixCls}-select-option-selected`]: selected, |
52 | }); | ||
53 | return <li className={cls} key={index} onClick={this.onSelect.bind(this, +item)}>{item}</li>; | ||
57 | }); | 54 | }); |
58 | }, | 55 | }, |
59 | 56 | ||